cugoj-1697
来源:2016华中师范大学“计蒜客杯”F题
题目大意:
给出n个点和m条边,再告诉你你个点中哪三个点是目标地点,然后要你求出连接这三个目标地点的最短路径。
解题思路:
这是最短路问题,先对三个目标地点求出其到所有其他点的最短路径,将结果保存后对所有的点进行一次遍历,计算该点到三个目标地点的距离之和,然后输出最小的结果。
为什么这么算能够确保结果是正确的呢?会不会有路径重叠的情况导致结果偏大?
*红色圈出部分即为重叠部分
我们可以具体讨论一下这中情况会不会发生:
当三个目标地点都能够互相连通时(不能连通则无解),必定能将两个目标地点先以最短路径连起来,这个时候我们再来考虑第三个目标地点,这时第三个点想要连入路径中只有两种方法,一是连在路径上,二是连在另外两个目标地点上。对于这两种情况都可以肯定的是路径中连入第三个目标地点的点到三个目标地点的路径都不会有重叠,而每种有重叠的情况都会有比其更好的不重叠的情况来代替。
解题要点:
由于n的大小为10^4,用二维数组储存图会超内存,所以这里采用邻接矩阵的储存方式。
dijkstra算法求最短路会导致超时,但经过观察后发现m的最大值为30000,是稀疏矩阵,所以用spfa算法求最短路。
附代码:
#include <iostream>
#include <vector>
#include <queue>
#include <stdio.h>
#include <string.h>
using namespace std;
struct road{
int b,c;
};
int n;
bool flag[10010];
int result[3][10010];
vector<road> map[10010];
void Find(int start,int o){
memset(flag,0,sizeof(flag));
flag[start] = 1;
int i,j,l,k,MIN,temp;
for(i=1;i<=n;i++){
result[o][i] = 999999999;
}
result[o][start] = 0;
queue<int> q;
q.push(start);
while(q.empty() != true){
temp = q.front();
q.pop();
for(i=0;i<map[temp].size();i++){
j = map[temp].at(i).c;
k = map[temp].at(i).b;
if(result[o][k]>result[o][temp]+j){
result[o][k] = result[o][temp]+j;
q.push(k);
}
}
}
}
int main(){
road temp;
int t,m,a,MIN,i,A,B,C;
cin>>t;
while(t--){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) map[i].clear();
for(i=0;i<m;i++){
scanf("%d%d%d",&a,&temp.b,&temp.c);
map[a].push_back(temp);
B = temp.b;
temp.b = a;
map[B].push_back(temp);
}
scanf("%d%d%d",&A,&B,&C);
Find(A,0);
Find(B,1);
Find(C,2);
MIN = 999999999;
for(i=1;i<=n;i++){
if(result[0][i] == 999999999 || result[1][i] == 999999999 || result[2][i] == 999999999) continue;
if(result[0][i]+result[1][i]+result[2][i]<MIN) MIN = result[0][i]+result[1][i]+result[2][i];
}
if(MIN == 999999999) cout<<"-1"<<endl;
else cout<<MIN<<endl;
}
return 0;
}