【ACM】- PAT. A1072 Gas Station 【图 - 最短路径】

题目链接
题目分析

给出M个加油站待选点和N个房屋,要求加油站距离最近的房子越远越好;(即最小距离中最大的那个)
当有相同解时,选择平均距离更小的那一个,若还有多解,选择编号更小的那一个

民房编号 1~N(1000); 气站编号G1~GM(10)

解题思路

对于所有候选站点,依次执行如下步骤:
Dijkstra()算法 查找最短路径,只需记录到每个结点的最短距离即可,不需要保存路径;每检查完一个待选点,判断:
1、检查是够全部在范围内,并记录最小距离
2、计算平均距离;
3、与已保存的最小距离比较

注意点:
1、候选气站点当也当作图中正常结点,其路径亦可使用;只是不要求其也在气站服务范围内!
2、最后一个测试点,候选站点的编号会达到 G10,所以转化序号时要注意,不能简单用 id = str[1] - '0'


AC程序(C++)
/**********************************
*@ID: 3stone
*@ACM: PAT.A1072 Gas Station
*@Time: 18/8/21
*@IDE: VSCode 2018 + clang++
***********************************/
#include
#include
#include
using namespace std;

const int maxn = 1030;
const int INF = 0x7fffffff;
int N, M, K;//民房数,待选气站数,边数,
int Max_Dis;//服务范围

int G[maxn][maxn];  //图
int d[maxn]; //最短距离
bool vis[maxn]; //标记是否访问

int Min_Dis, Min_Sum_Dis;
int best_station; 

void Dijkstra(int s) {
    //初始化起点
    d[s] = 0.0; 
    for(int i = 1; i <= N + M; i++) { //遍历n+m次,每次取一个点
        //选择最近点
        int u = -1;
        int temp_min = INF;
        for(int j = 1; j <= N + M; j++) {
            if(vis[j] == false && d[j] < temp_min) {
                u = j;
                temp_min = d[j];
            }
        }
        if(u == -1) return;
        vis[u] = true;
        //更新最短距离 & 花费
        for(int v = 1; v <= N + M; v++) {
            if(vis[v] == false && G[u][v] != INF) { //只需保存最短路径长度即可
                if(d[u] + G[u][v] < d[v]) {
                    d[v] = d[u] + G[u][v];
                }  
            }
        }//for - v
    }//for - i

}//Dijkstra


void min_check(int s) {
    int temp_min = INF;
    int sum_dis = 0;
    for(int i = 1; i <= N; i++ ) {
        if(d[i] > Max_Dis) return; //有结点在服务区外,舍弃这个候选点
        sum_dis += d[i];
        if(d[i] < temp_min){
            temp_min = d[i];
        }

    }
    if(temp_min > Min_Dis) {
        best_station = s;
        Min_Dis = temp_min;
        Min_Sum_Dis = sum_dis;
    } else if(temp_min == Min_Dis && sum_dis < Min_Sum_Dis) {
        best_station = s;
        Min_Sum_Dis = sum_dis;
    }
    //还有多解,选择编号小的;本身就是从小到大check的,不用再加判断;
}

int get_id(char str[]) {

    int len = strlen(str);
    int ID = 0, i = 0;
    if(str[0] == 'G') i = 1;
    while(i < len)
        ID = ID * 10 + (str[i++] - '0');

    if(str[0] == 'G') return ID + N;
    else return ID;
}

int main() {
    char s1[5], s2[5]; 
    int c1, c2;
    int route_dis;
    while(scanf("%d %d %d %d", &N, &M, &K, &Max_Dis) != EOF) {

        //初始化
        fill(G[0], G[0] + maxn * maxn, INF);
        Min_Dis = -1;
        Min_Sum_Dis = -1;
        best_station = -1;

        //输入路径信息
        for(int i = 0; i < K; i++) {
            scanf("%s %s %d", s1, s2, &route_dis);

            c1 = get_id(s1);
            c2 = get_id(s2);
            G[c1][c2] = route_dis;//无向图
            G[c2][c1] = route_dis;
        }

        for(int i = 1; i <= M; i++) { //依次检查各个待选点
            //初始化
            fill(vis, vis + maxn, false);
            fill(d, d + maxn, INF);

            Dijkstra(N + i);

            min_check(N + i);
        }

        if(best_station == - 1) 
            printf("No Solution\n");
        else {
            printf("G%d\n", best_station - N);
            printf("%.1f %.1f\n", (double)Min_Dis, (double)Min_Sum_Dis / N);

        }

    }//while

    return 0;
}

你可能感兴趣的:(PAT,ACM-图)