目录
1,题目描述
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
题目大意
从给定的几个站点中,寻找最合适的加油站站点。该站点需满足:距离所有house不得超过服务范围Ds;取加油站距离最近house(距离为minDis)最大的的站点;若不唯一,则取加油站距所有house平均距离最小的站点 ;仍不唯一,取id最小的。
注意:
- the minimum distance between the station and any of the residential housing is as far away as possible:这是选择加油站的第一参考(优先级最高):距离house最短距离越大越好;
- If there are more than one solution, output the one with the smallest average distance to all the houses:第二参考,平均距离越小越好
2,思路
数据结构
- int N, M, K, Ds; //N房子数目 M侯选位置数目 K路的数目 Ds加油站服务范围
- struct node{int id; float minDis = INT_MAX, maxDis = 0, totalDis = 0;}:候选站点的编号、与最近house的距离、与最远house的距离(判断是否满足条件时用)、距离所有house距离之和;
- int graph[1020][1020]:存放所有路径距离信息(站点与站点,站点与house,house与house);
- int dis[1020]:(Dijkstra算法基本操作)在每次Dijkstra算法中,存放起点与各点之间的最短距离;
- bool visited[1020]:(Dijkstra算法基本操作)判断节点是否已被访问;
- vector<node> ans:存放符合基本条件(所有house都在加油站的服务范围之内)的站点信息;
算法
- 将所有的house和station看作点,构造图graph(节点的编号先按字符串处理,若第一个字符为'G'则消去首字符,其余字符转换为数字,并且加上N(输出时,减去N即可还原) ;否则直接转换为数字即可);
- 设计Dijkstra函数,并且统计与最近house的距离minDis、与最远house的距离(判断是否满足条件时用)maxDis、距离所有house距离之和totalDis(计算平均距离)
- 对每一个候选站点都使用一次Dijkstra算法,将所有符合基本条件的站点存入ans中;
- 按照排序函数进行排序;
- 若ans为空,输出No Solution;否则输出ans[0]相关信息即可;
3,AC代码
#include<bits/stdc++.h>
using namespace std;
struct node{
//id指明哪个站点 minDis距离house的最短距离 maxDis加油站距house最远距离 totalDis距离house的总距离
int id;
float minDis = INT_MAX, maxDis = 0, totalDis = 0;
};
vector<node> ans; //存放合适站点的信息
int N, M, K, Ds; //N房子数目 M侯选位置数目 K路的数目 Ds加油站服务范围
int graph[1020][1020];
int dis[1020];
bool visited[1020];
void Dijkstra(int start){
fill(dis, dis + 1020, INT_MAX);
fill(visited, visited + 1020, false);
node n;
n.id = start;
dis[start] = 0;
for(int i = 1; i <= N + M; i++){
int minDis = INT_MAX, u = -1;
for(int j = 1; j <= N + M; j++){
if(visited[j] == false && dis[j] < minDis){
u = j;
minDis = dis[j];
}
}
if(u == -1) break;//
visited[u] = true;
if(u <= N){ //将加油站与house相连的路进行判断更新
n.totalDis += 1.0 * minDis;
if(minDis < n.minDis) n.minDis = 1.0 * minDis;
if(minDis > n.maxDis) n.maxDis = 1.0 * minDis;
}
for(int v = 1; v <= M + N; v++){
if(visited[v] == false && graph[u][v] != INT_MAX){
if(dis[v] > dis[u] + graph[u][v]){
dis[v] = dis[u] + graph[u][v];
}
}
}
}
if(n.maxDis <= Ds) ans.push_back(n);
}
bool cmp1(node a, node b){
if(a.minDis != b.minDis)
return a.minDis > b.minDis;
else{
if(a.totalDis != b.totalDis)
return a.totalDis < b.totalDis;
else return a.id < b.id;
}
}
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE
string s1, s2;
int a, b, d;
scanf("%d %d %d %d", &N, &M, &K, &Ds);
fill(graph[0], graph[0] + 1020 * 1020, INT_MAX);
for(int i = 0; i < K; i++){
cin>>s1>>s2>>d;
if(s1[0] == 'G'){
s1 = s1.substr(1); //提取从1往后的字符串
a = N + stoi(s1);
}
else a = stoi(s1);
if(s2[0] == 'G'){
s2 = s2.substr(1);
b = N + stoi(s2);
}
else b = stoi(s2);
if(graph[a][b] > d) //两点之间可能不止一条路 选择最短的一条
graph[a][b] = graph[b][a] = d;
}
for(int i = N + 1; i <= N + M; i++)
Dijkstra(i);
if(ans.size() == 0) printf("No Solution");
else{
sort(ans.begin(), ans.end(), cmp1);
printf("G%d\n%.1f %.1f", ans[0].id-N, ans[0].minDis, ans[0].totalDis / N);
}
return 0;
}
4,解题过程
第一搏
字符串转数字废了好多时间(还是不够熟悉。。。),不熟悉的同学可以看这里@Miserable_ccf【C++中的字符串(String)和数值转换】;
本想使用原先的方法,确定string类型变量的大小(resize()),再使用scanf函数(速度比较快)。然而会这样:
改用cin后就可以了:
第二搏
程序修改后基本没什么问题了,但是在四舍五入输出的时候却出现了奇怪现象:
codeblock中运行后,3.25输出的是3.2
(上网查了下,数据输出时,自动四舍五入)提交时果然全部通过。
可能是编译器选项设置的问题吧。。。(小本本记下了)