1

题目大意:

现在有N座房子,M个加油站待选择点,K条边,现在要在M个加油站待选择点选择一个加油站出来,要求满足距离N个房子尽可能远但是同时也得保证房子均在服务范围Ds中,如果有多个选择平均距离最小的,如果还有多个,选择编号最小的 。

算法思路:

典型的最短距离求解问题,使用Dijkstra算法完成即可,这里得先了解我们需要获得什么样的信息,我们通过Dijkstra算法可以获得当前加油站到所有房子的最短距离,由dis数组保存,然后我们需要获得以下3个信息:

1.所有加油站中距离房子最短的距离并且处于服务范围内中的最大距离(对应于加油站距离N个房子尽可能远)
2.当前加油站到所有房子的平均最短距离,在1中的距离相等的时候选择最小的(通过dis数组可以轻易获得)
3.加油站的编号(这个最好得到,每次执行Dijkstra算法的起点就是,根据1和2进行更新,否则不更新)

知道以上三点后,我们就可以进行如下操作:
1、每一个加油站节点GM的节点的编号为N+M。
2、对于M个待选加油站我们每次都进行 Dijkstra算法获得当前加油站到其他点的最短距离,保存在dis数组中,然后判断当前加油站距离房子的距离是否有超过服务范围的,如果没有那么就记录当前加油站到房子的最短距离local_min_dis、到所有房子的平均最短距离(这里使用总长度来代替)total_dis以及当前节点编号i,最后再重置visited数组就好,如果有超过范围的,直接pass当前加油站。
3、如果当前的最短距离比全局最短距离更大或者相等但是总距离比全局总距离更小,更新全局最短距离global_max_dis,总距离result_total_dis和站点编号result_index。
4、如果所有的加油站都超过服务返回就输出No Solution,这里使用result_index是否等于-1来进行判断。

注意点:

  • 1、每一次获取最短路径需要初始化visited
  • 2、输出节点编号的时候得减去N
  • 3、获取最短路径的时候,需要进行N+M次,也即是得加油站本身也是一个真实存在的地点,可以用来优化最短路径。
  • 4、最后保存1位小数输出的最短平均距离输出值和测试用例不一样但是在提交过程中没有任何问题。
  • 5、最后一个测试点出现运行时错误是输入里面有G10,得取G后面的字符串然后转化为数字,而不是直接取后面一位,出现段错误,是因为maxn设置太小了,出现答案错误说明,判断输入是否为G开头的方法出错,不能使用字符串的长度进行判断,只能用第一个字符是否为G来进行判断,因为数子是从1位到4位变化,G后面数字是从1到2位变化的,或者maxn设置为了1010及其以下的数字,最小得设置为1011才能通过.

提交结果:

截屏2020-11-24 下午3.48.44.png

AC代码:

#include<cstdio>
#include<vector>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>

using namespace std;

int N,M,K,Ds;//房子数目,候选站点数目,道路数目,最大服务距离
int G[1200][1200];// 边权,表示距离
bool visited[1200];// 访问标记数组 
int dis[1200];// 每一个居民距离起点的最短距离

void Dijkstra(int start){
    // 初始化 
    fill(dis,dis+1200,0x3fffffff);
    dis[start] = 0;
    // 循环N+M次
    for(int i=1;i<=N+M;++i){
        // 首先从所有未访问的点中找到距离起点最小的点 
        int nearest,minDis=0x3fffffff;
        for(int j=1;j<=N+M;++j){
            if(!visited[j]&&minDis>dis[j]){
                nearest = j;
                minDis = dis[j];
            }
        }
        if(minDis==0x3fffffff) return ;// 没有找到,说明其他点不再连通 
        // 优化借助nearest到其他未访问的点的距离
        visited[nearest] = true;
        for(int j=1;j<=N+M;++j){
            if(!visited[j]&&G[nearest][j]!=0&&dis[j]>dis[nearest]+G[nearest][j]){
                dis[j] = dis[nearest]+G[nearest][j];
            }
        }
    } 
}

int main(){
    scanf("%d %d %d %d",&N,&M,&K,&Ds);
    string a,b;
    int dist;
    int index_a,index_b;
    for(int i=0;i<K;++i){
        cin>>a>>b>>dist;
        if(a[0]=='G'){
            index_a = stoi(a.substr(1))+N;// G1为N+1 
        }else {
            index_a = stoi(a);
        }
        if(b[0]=='G'){
            index_b = stoi(b.substr(1))+N;
        }else{
            index_b = stoi(b);
        }
        G[index_a][index_b] = G[index_b][index_a] = dist;
    }
    // 求出每一个站点作为起点,其他房子距离它的最短距离
    int result_index = -1;// 最优加油站
    int global_max_dis = -1;// 所有最短距离中的最大距离
    int result_total_dis = 0x3fffffff;// 最优加油站到所有居民房的总距离 
    for(int i=N+1;i<=N+M;++i){
        memset(visited,0,sizeof(visited));// 每一次遍历都得初始化访问标记数组 
        Dijkstra(i);
        // 获得居民房到当前结点的最短距离后,需要获得当前加油站到所有居民房的最短距离,平均最短距离(这里使用总距离代替)
        int local_min_dis = 0x3ffffff;
        int total_dis = 0;
        bool isOutOfRange = false;
        for(int j=1;j<=N;++j){
            if(dis[j]>Ds){
                isOutOfRange = true;
                break;
            }
            local_min_dis = min(local_min_dis,dis[j]);
            total_dis += dis[j];
        } 
        
        if(isOutOfRange) continue;// 当前加油站无法为所有居民提供服务
        if(global_max_dis<local_min_dis){
            result_index = i;
            global_max_dis = local_min_dis;
            result_total_dis = total_dis;
        }else if(global_max_dis==local_min_dis&&result_total_dis>total_dis){
            result_index = i;
            result_total_dis = total_dis;
        }
    }
    
    if(result_index==-1){
        printf("No Solution");
        return 0;
    }
    printf("G%d\n",result_index-N);
    printf("%.1lf %.1lf",global_max_dis*1.0,result_total_dis*1.0/N);
    return 0;
}

乔梓鑫
569 声望17 粉丝

主要分享个人学习经验和心得