HDU - 2066 最短路+加一个节点
一个图上,有M条边,Z个出发点,Y个终止点。求一条最短路,其中起点是Z中的任意一点,终点是Y中任意一点。
Input
输入数据有多组,输入直到文件结束。
每组的第一行是三个整数M,Z,Y
接着有M行,每行有三个整数a,b,w,表示a,b之间存在一条长度为w的边 (1=<(a,b)<=1000,w原题干未给范围c++ int够用),可能存在重边,边为双向边。
接着的第M+1行有Z个数,表示起点标号
接着的第M+2行有Y个数,表示终点标号
Output
每组数据,输出一个整数占一行表示最短路的长度
Sample Input
6 2 3 1 3 5 1 4 7 2 8 12 3 8 4 4 9 12 9 10 2 1 2 8 9 10
Sample Output
9
代码(邻接矩阵):
1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #include<algorithm> 5 #include<queue> 6 #include<vector> 7 using namespace std; 8 const int maxn=1005; 9 typedef long long ll; 10 #define INF 0x3f3f3f3f 11 struct shudui1 12 { 13 ll start,value; 14 bool operator < (const shudui1 q)const 15 { 16 return value<q.value; 17 } 18 } str1; 19 struct shudui2 20 { 21 ll start,value; 22 } str2; 23 ll v1[maxn],v2[maxn],v[maxn],k[maxn][maxn]; 24 priority_queue<shudui1>r; 25 void JK(ll maxx) 26 { 27 memset(v,INF,sizeof(v)); 28 v[0]=0; 29 str1.start=0; 30 str1.value=0; 31 r.push(str1); 32 while(!r.empty()) 33 { 34 ll x,y; 35 str1=r.top(); 36 r.pop(); 37 x=str1.start; 38 y=str1.value; 39 if(v[x]<y) continue; 40 //说明在这个点再此之后又入队了 41 //此次出队的并不是s到这个点的最短路, 42 //所以在这次更新前点v所连的点已经更过一次了 43 //所以后面也不会进行松弛操作 44 for(ll i=0; i<=maxx; ++i) 45 { 46 if((v[x]+k[x][i]<v[i])) 47 { 48 v[i]=v[x]+k[x][i]; 49 str1.start=i; 50 str1.value=v[i]; 51 r.push(str1); 52 } 53 } 54 } 55 } 56 57 int main() 58 59 { 60 61 ll n,m,p,a,b,c; 62 63 while(scanf("%lld %lld %lld",&n,&m,&p)!=EOF) 64 65 { 66 67 memset(k,INF,sizeof(k)); 68 69 ll maxx=n; 70 71 for(ll i=1; i<=n; i++) 72 73 { 74 75 scanf("%lld %lld %lld",&a,&b,&c); 76 77 maxx=max(maxx,max(a,b)); //记录顶点上限 78 79 k[a][b]=min(c,k[a][b]); //保存最小边权 80 81 k[b][a]=min(c,k[a][b]); 82 83 } 84 85 for(ll i=0; i<m; i++) 86 87 { 88 89 scanf("%lld",&a); 90 91 k[a][0]=k[0][a]=0; 92 93 } 94 95 for(ll i=0; i<p; i++) 96 97 scanf("%lld",&v2[i]); 98 99 ll minn=INF; 100 101 JK(maxx); 102 103 for(ll j=0; j<p; j++) //找到最小路径 104 105 if(v[v2[j]]<minn) 106 107 minn=v[v2[j]]; 108 109 printf("%lld\n",minn); 110 111 } 112 113 }
代码(邻接表)+注释:
1 /* 2 这道题首先不能暴力枚举起点和终点,所以floyd就不用再用了 3 4 我们只需要弄一个新节点(我代码中是maxx),让起点和新节点连一条边,边的距离为0,这样的话只需要跑一边迪杰斯特拉算法后,在v数组中枚举终点,取最小值就可以了 5 6 错了这么多次,才发现原来我是没有清空vector容器。。。 7 用邻接表存图是不用考虑有重边这个问题的 8 9 */ 10 #include<stdio.h> 11 #include<string.h> 12 #include<iostream> 13 #include<algorithm> 14 #include<queue> 15 #include<vector> 16 using namespace std; 17 const int maxn=1005; 18 typedef long long ll; 19 #define INF 0x3f3f3f3f 20 struct shudui1 21 { 22 ll start,value; 23 bool operator < (const shudui1 q)const 24 { 25 return value<q.value; 26 } 27 } str1; 28 struct shudui2 29 { 30 ll start,value; 31 } str2; 32 ll v1[maxn],v2[maxn],v[maxn]; 33 priority_queue<shudui1>r; 34 vector <shudui2>w[2005]; 35 void JK(ll s) 36 { 37 memset(v,INF,sizeof(v)); 38 v[s]=0; 39 str1.start=s; 40 str1.value=0; 41 r.push(str1); 42 while(!r.empty()) 43 { 44 ll x,y; 45 str1=r.top(); 46 r.pop(); 47 x=str1.start; 48 y=str1.value; 49 if(v[x]<y) continue; 50 //说明在这个点再此之后又入队了 51 //此次出队的并不是s到这个点的最短路, 52 //所以在这次更新前点v所连的点已经更过一次了 53 //所以后面也不会进行松弛操作 54 ll len=w[x].size(); 55 for(ll i=0; i<len; ++i) 56 { 57 str2=w[x][i]; 58 if((v[x]+str2.value<v[str2.start])) 59 { 60 v[str2.start]=v[x]+str2.value; 61 str1.start=str2.start; 62 str1.value=v[str2.start]; 63 r.push(str1); 64 } 65 } 66 } 67 } 68 int main() 69 { 70 ll a,z,y,d,f,g; 71 while(~scanf("%lld%lld%lld",&a,&z,&y)) 72 { 73 ll maxx=0; 74 while(a--) 75 { 76 scanf("%lld%lld%lld",&d,&f,&g); 77 maxx=max(maxx,max(d,f)); 78 str2.start=f; 79 str2.value=g; 80 w[d].push_back(str2); 81 str2.start=d; 82 str2.value=g; 83 w[f].push_back(str2); 84 } 85 maxx+=1; 86 for(ll i=1; i<=z; ++i) 87 { 88 scanf("%lld",&v1[i]); 89 str2.start=v1[i]; 90 str2.value=0; 91 w[maxx].push_back(str2); 92 str2.start=maxx; 93 str2.value=0; 94 w[v1[i]].push_back(str2); 95 } 96 97 for(ll i=1; i<=y; ++i) 98 scanf("%lld",&v2[i]); 99 ll minn=INF; 100 101 JK(maxx); 102 for(ll j=1; j<=y; ++j) 103 { 104 minn=min(minn,v[v2[j]]); 105 } 106 107 printf("%lld\n",minn); 108 for(int i=1;i<=maxx;++i) 109 { 110 w[i].clear(); 111 } 112 } 113 return 0; 114 }