城市平乱(nyoj 115)单源最短路径

8 篇文章 0 订阅

城市平乱

时间限制: 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


关于单源最短路径,在百科里看到的解题思路,稍作整理:

将图G中所有的顶点V分成两个顶点集合S和T。以v为源点已经确定了最短路径的终点并入S集合中,S初始时只含顶点v,T则是尚未确定到源点v最短路径的顶点集合。然后每次从T集合中选择S集合点中到T路径最短的那个点,并加入到集合S中,并把这个点从集合T删除。直到T集合为空为止。
具体步骤:
1、选一顶点v为源点(起点),并视从源点v出发的所有边为到各顶点的最短路径(因为求的是最短路径,所以就要用一个记录从源点v到其它各顶点的路径长度dis[],开始时,dis是源点v到顶点i的直接边长度,即dis中记录的是邻接矩阵(二维数组)的第v行。)
2、在上述的最短路径dis[]中选一条最短的,并将其终点k加入到集合s中,标记从起点到k已经找到最短路径。
3、调整T中各顶点到源点v的最短路径。 因为当顶点k加入到集合s中后,源点v到T中剩余的其它顶点j就又增加了经过顶点k到达j的路径,这条路径可能要比源点v到j原来的最短的还要短。调整方法是比较dis[k]+g[k,j]与dis[j],取其中的较小者。
4、再选出一个到源点v路径长度最小的顶点k,从T中删去后加入S中,再回去到第三步,如此重复,直到集合S中的包含图G的所有顶点。

对于这一题,只要将叛乱城市作为起点,求出到其余城市的最短时间,再在有军队的城市中选出时间最短的输出即可。

#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAX 1000000
int N, M, P, Q;
int army[110];
int map[1100][1100];
int vis[1100];
int dis[1100];
/*
void init()
{
	int i, j;
	for(i = 0; i < 1100; i++)
		for(j = 0; j < 1100; j++)
			map[i][j] = MAX;
}
*/
void dijkstra(int start)
{
	int i, j, min, k;
	for(i = 1; i <= M; i++)
		dis[i] = map[start][i];

	dis[start] = 0;//到本身的路径长度为0
	vis[start] = 1;//起点标记成已访问过
	for(i = 1; i <= M; i++)
	{
		min = MAX;
		k = 0;
		for(j = 1; j <= M; j++)
		{
			if(!vis[j] && min > dis[j])
			{//选出当前的最短路径
				min = dis[j];
				k = j;
			}
		}
		if(min == MAX)
			break;
		vis[k] = 1;
		for(j = 1; j <= M; j++)//更新路径
			if(!vis[j] && dis[j] > min + map[k][j])
				dis[j] = min + map[k][j];
		//起初我将map的所有值都初始化成INT_MAX,因此如果map[k][j]的值还是INT_MAX,加上min就会溢出,结果错误
	}
}

int main (void)
{
	int T, i;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d %d %d %d", &N, &M, &P, &Q);
		//init();
		memset(map, MAX, sizeof(map));
		for(i = 0; i < N; i++)
			scanf("%d", &army[i]);
		int a, b, c;
		for(i = 0; i < P; i++)
		{
			scanf("%d %d %d", &a, &b, &c);
			if(map[a][b] > c)
				map[a][b] = map[b][a] = c;
		}
		dijkstra(Q); 
		int min = MAX;
		for(i = 0; i < N; i++)
		{
		//	printf("%d\n", dis[army[i]]);
			if(dis[army[i]] < min)
				min = dis[army[i]];
		}

	    printf("%d\n", min);
		memset(vis, 0, sizeof(vis));
	}
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值