城市平乱
时间限制:1000 ms | 内存限制:65535 KB
难度:4
描述
南将军统领着N个部队,这N个部队分别驻扎在N个不同的城市。
他在用这N个部队维护着M个城市的治安,这M个城市分别编号从1到M。
现在,小工军师告诉南将军,第K号城市发生了暴乱,南将军从各个部队都派遣了一个分队沿最近路去往暴乱城市平乱。
现在已知在任意两个城市之间的路行军所需的时间,你作为南将军麾下最厉害的程序员,请你编写一个程序来告诉南将军第一个分队到达叛乱城市所需的时间。
注意,两个城市之间可能不只一条路。
输入
第一行输入一个整数T,表示测试数据的组数。(T<20)
每组测试数据的第一行是四个整数N,M,P,Q(1<=N<=100,N<=M<=1000,M-1<=P<=100000)其中N表示部队数,M表示城市数,P表示城市之间的路的条数,Q表示发生暴乱的城市编号。
随后的一行是N个整数,表示部队所在城市的编号。
再之后的P行,每行有三个正整数,a,b,t(1<=a,b<=M,1<=t<=100),表示a,b之间的路如果行军需要用时为t
数据保证暴乱的城市是可达的。
输出
对于每组测试数据,输出第一支部队到达叛乱城市时的时间。每组输出占一行
样例输入
1
3 8 9 8
1 2 3
1 2 1
2 3 2
1 4 2
2 5 3
3 6 2
4 7 1
5 7 3
5 8 2
6 8 2
样例输出
4
来源
《世界大学生程序设计竞赛高级教程·第一册》改编
上传者
张云聪
思路:将目标城市q看做是起始点,n个起始城市看做是目标城市,逆向思维,就可以了,很简单,不过只能用迪杰斯特拉,因为弗洛伊德会超时。
AC代码:
#include<bits/stdc++.h>
#define inf 0x3f3f3f
#define maxn 1005
int vis[maxn], maps[maxn][maxn];
int dis[maxn],star[maxn];
using namespace std;
int main()
{
int n,m,q,p;
//n个起始城市,一共m个城市,p条路,q是目标城市;
int a,b,c;//存图用;
int CASE;//CASE组数据;
scanf("%d",&CASE);
while(CASE--)
{
memset(star,0,sizeof(star));
scanf("%d%d%d%d",&n,&m,&p,&q);
//n armies, m city, p ways,Q aim;
memset(maps,0,sizeof(maps));
//存图的数组c初始化;
for(int i=1; i<=1000; i++)
for(int j=1; j<=1000; j++)
if(i!=j)
maps[i][j]=inf;
//起始城市也就是目标城市;
for(int i=1; i<=n; i++)
{
scanf("%d",&star[i]);
maps[star[i]][0]=maps[0][star[i]]=0;
}
//存图 , p条路;
for(int i=1; i<=p; i++)
{
scanf("%d%d%d",&a,&b,&c);
if(maps[a][b]>c)
maps[a][b]= maps[b][a]=c;
}
//vis数组初始化;
memset(vis,0,sizeof(vis));
//dis数组初始化;
for(int i=0; i<=m; i++)
dis[i]=maps[q][i];
//寻找以q点为顶点到各个点的距离;
vis[q]=1;
//迪杰斯特拉代码核心;
for(int i=1; i<=m-1; i++)
{
int minm=inf,u;
for(int j=1; j<=m; j++)
if(vis[j]==0&&dis[j]<minm)
{
minm=dis[j] ;
u=j;
}
vis[u]=1;
for(int v=1; v<=m; v++)
if(maps[u][v]<inf)
if(dis[v]>dis[u]+maps[u][v])
dis[v]=dis[u]+maps[u][v];
}
int minn=inf;
//寻找q点到各目标城市的最小距离;
for(int i=1;i<=n;i++)
if(dis[star[i]]<minn)
minn=min(minn,dis[star[i]]);
printf("%d\n",minn);
}
return 0;
}