PAT甲级刷题记录——1072 Gas Station (30分)

A gas station has to be built at such a location that the minimum distance between the station and any of the residential housing is as far away as possible. However it must guarantee that all the houses are in its service range.

Now given the map of the city and several candidate locations for the gas station, you are supposed to give the best recommendation. If there are more than one solution, output the one with the smallest average distance to all the houses. If such a solution is still not unique, output the one with the smallest index number.

Input Specification:

Each input file contains one test case. For each case, the first line contains 4 positive integers: N (≤103​​ ), the total number of houses; M (≤10), the total number of the candidate locations for the gas stations; K (≤104 ), the number of roads connecting the houses and the gas stations; and DS​​ , the maximum service range of the gas station. It is hence assumed that all the houses are numbered from 1 to N, and all the candidate locations are numbered from G1 to GM.
Then K lines follow, each describes a road in the format

P1 P2 Dist

where P1and P2are the two ends of a road which can be either house numbers or gas station numbers, and Distis the integer length of the road.

Output Specification:

For each test case, print in the first line the index number of the best location. In the next line, print the minimum and the average distances between the solution and all the houses. The numbers in a line must be separated by a space and be accurate up to 1 decimal place. If the solution does not exist, simply output No Solution.

Sample Input 1:

4 3 11 5
1 2 2
1 4 2
1 G1 4
1 G2 3
2 3 2
2 G2 1
3 4 2
3 G3 2
4 G1 3
G2 G1 1
G3 G2 2

Sample Output 1:

G1
2.0 3.3

Sample Input 2:

2 1 2 10
1 G1 9
2 G1 20

Sample Output 2:

No Solution

思路

做了半天终于AC了,这题给我的感觉是又坑又难……
首先,我们来说说难的地方

①要读懂题目意思,我一开始就不懂,什么叫“ the minimum distance between the station and any of the residential housing is as far away as possible”,又要最短又要最远??后来看了一眼《上机训练实战指南》才知道,原来这意思是每个加油站到居民楼最短距离中取最大者……
②三个标尺(其实也不是完全意义上的三个,也可以算两个):

  • 标尺1:最优先应该选择离居民楼最短距离中最大的那个加油站;
  • 标尺2:如果恰巧有两个及以上加油站满足上面的那个标尺(最短距离一样都是最大的),那么选择平均距离最小者;
  • 标尺3:如果有两个及以上加油站满足上面两个标尺(天啦撸!!),那么选择编号最小者。

光看这个标尺就会感觉很头疼,其实仔细想一下,第三个标尺要选择编号最小者,那我后面碰到的不更新不就完事了,于是差不多这题就是两个标尺的问题了。

其次,我们来说说坑的地方

①样例给的有问题,我几乎写完一遍代码就把样例过了,但是!!但是!!样例上运行出来的平均距离数值完全是3.2嘛!(我用setprecision和%.1f都试过,两种方法都是输出3.2,只有3.26的时候才会输出3.3,但是我看晴神的题解、柳神的题解,都没有专门解决这个问题,都只是%.1f输出就过了)然而经过我的多次验证,事实证明,测试点里没有样例……大家放心些setprecision或者%.1f就好了,不用管这个问题;
②反复使用Dijkstra数组重置的问题,我一开始把memset()写在了对加油站遍历的for循环的最后(好像没什么问题??),但是有一个点会出错(一直WA了两个测试点,我到最后才发现T T),就是说如果当前的加油站不可取(即:有一个d[i]的值比题目给出的范围Ds要大),那么直接continue进入下一轮循环,这句话也没什么问题,但是两句话放在一起就有问题了。如果直接continue进入下一轮循环,那么势必之前进行了一次Dijkstra的数组还会残留在那里,因为并没有继续读后面的memset()函数,所以就会造成答案错误。这里我的建议是干脆写在Dijkstra函数的开头好了,不管你for循环怎么改,这么写总是没问题的。
③Dijkstra时一定要面向所有点(包括加油站和居民楼),而且本题是从1开始编号的,不要惯性思维直接把j=0;j

其他就没什么了,这题的基本思路就是对每个加油站做一次Dijkstra,然后每做完一次更新一下题目需要输出的量,最后对加油站遍历结束,也就得到了我们想要的结果。

至于怎么将G1等转换为编号呢,很简单,把G删了,然后后面的数字加上N,这样加油站的编号就紧跟在居民楼编号的后面了,于是,输出的时候只要先输出一个“G”,再把当前编号减去N,就是我们要的加油站了。

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1011;
const int INF = 123123123;
struct node{
    int v;
    double dis;
};
vector<node> G[maxn];
double d[maxn] = {0};
bool vis[maxn] = {false};
void Dijkstra(int n, int s){//这里的n代表的是总的顶点数
    memset(vis, false, sizeof(vis));//开头记得清空相关数组
    fill(d, d+maxn, INF);
    d[s] = 0;
    for(int i=0;i<n;i++){
        int u = -1;
        int MIN = INF;
        for(int j=1;j<=n;j++){//这里不要忘了改,顶点从1开始编号
            if(vis[j]==false&&d[j]<MIN){
                u = j;
                MIN = d[j];
            }
        }
        if(u==-1) return;
        vis[u] = true;
        for(int j=0;j<G[u].size();j++){
            int v = G[u][j].v;
            if(vis[v]==false){
                if(d[u]+G[u][j].dis<d[v]){
                    d[v] = d[u]+G[u][j].dis;
                }
            }
        }
    }
}
int main(){
    int N, M, K, Ds;
    cin>>N>>M>>K>>Ds;
    for(int i=0;i<K;i++){
        string tmpa, tmpb;
        int a, b;
        double tmpdis;
        cin>>tmpa>>tmpb>>tmpdis;
        if(tmpa[0]=='G'){
            string tmp = tmpa.substr(1);//第二个参数不写默认切割到最后
            a = stoi(tmp)+N;//加油站的编号从N+1~N+M
        }
        else a = stoi(tmpa);
        if(tmpb[0]=='G'){
            string tmp = tmpb.substr(1);
            b = stoi(tmp)+N;
        }
        else b = stoi(tmpb);
        node temp;
        temp.dis = tmpdis;
        temp.v = a;
        G[b].push_back(temp);
        temp.v = b;
        G[a].push_back(temp);
    }
    double maxMin = -1;//寻找所有最短距离中最大的那个
    int resultGas = -1;//记录最终加油站的编号
    double minAvg = 123123123;
    for(int i=N+1;i<=N+M;i++){
        bool flag = true;//用来判断当前加油站是否可选
        int sum = 0;
        double avg = 0;
        Dijkstra(N+M, i);//对每个加油站做一遍Dijkstra
        double minDis = INF;
        for(int i=1;i<=N;i++){
            if(d[i]>Ds){
                flag = false;
                break;//如果某个距离超出了范围,那么这个加油站就不可取
            }
            else{//如果可取
                sum += d[i];
                if(d[i]<minDis) minDis = d[i];//寻找加油站到居民楼的最短距离
            }
        }
        if(flag==false) continue;//如果不可选,直接进入下一轮循环
        if(minDis>maxMin){//第一标尺
            maxMin = minDis;
            avg = (double)sum/N;//计算平均值
            minAvg = avg;//更新平均距离
            resultGas = i;
        }
        else if(minDis==maxMin){
            avg = (double)sum/N;//计算平均值
            if(avg<minAvg){
                minAvg = avg;
                resultGas = i;
            }
        }
    }
    if(resultGas==-1) cout<<"No Solution";
    else{
        cout<<'G'<<resultGas-N<<'\n';
        cout<<fixed<<setprecision(1)<<maxMin<<' ';
        cout<<fixed<<setprecision(1)<<minAvg;
    }
    return 0;
}

你可能感兴趣的:(PAT甲级)