Silver Cow Party(单源最短路:单向边的来回最短路的运用)

题目1:本题题目提交链接这个题用邻接矩阵来写就行,顶点少
题目2:提交链接,这个题必须用邻接表来写,顶点太多

One cow from each of N farms (1 ≤ N ≤ 1000) conveniently numbered 1…N is going to attend the big cow party to be held at farm #X (1 ≤ X ≤ N). A total of M (1 ≤ M ≤ 100,000) unidirectional (one-way roads connects pairs of farms; road i requires Ti (1 ≤ Ti ≤ 100) units of time to traverse.

Each cow must walk to the party and, when the party is over, return to her farm. Each cow is lazy and thus picks an optimal route with the shortest time. A cow’s return route might be different from her original route to the party since roads are one-way.

Of all the cows, what is the longest amount of time a cow must spend walking to the party and back?

Input
Line 1: Three space-separated integers, respectively: N, M, and X
Lines 2…M+1: Line i+1 describes road i with three space-separated integers: Ai, Bi, and Ti. The described road runs from farm Ai to farm Bi, requiring Ti time units to traverse.
Output
Line 1: One integer: the maximum of time any one cow must walk.

Sample Input

4 8 2
1 2 4
1 3 2
1 4 7
2 1 1
2 3 5
3 1 2
3 4 4
4 2 3

Sample Output

10

Hint
Cow 4 proceeds directly to the party (3 units) and returns via farms 1 and 3 (7 units), for a total of 10 time units.

题意:
n个农场,编号1到n,现在每个农场派出一头牛去参加聚会,举办地为x农场。每头牛都很懒,都想花费来回花费时间总时间最短,(怎么最短呢,当然是去的时候用时少,回来时也用时少啊),现需要求出在所有牛中,来回花费总时间最多的牛所花费的时间
注意:单向边,来回路程不一定一致

思路:
单源最短路,用两次,
1、先求顶点x到各个顶点的最短路
2、再求各个顶点到x的最短路(其实只需要把所有边翻转再次求x到各个顶点的最短路即可)
若不懂代码:请看啊哈算法一书
代码

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;

int e[1100][1100];
int book[1100];//book[i]标记为1,说明顶点i到源点的最短路已确定,即dis[i]不可能更小的了,若book[i]标记仍为0,说明此刻的dis[i]还有可能变得更小。
int dis[1100];//用来存储源点到各个点的距离
int dis1[1100];
int n,m,x;
/* 
e[i][j]用来存储顶点i到顶点j的距离,
规定顶点i到其本身的距离为0,
规定顶点i不能直接到达顶点j的距离为无穷大

数组book,book[i]==1表示顶点i不得再被使用,
book[i]==0表示顶点i可以被使用 

数组dis1,用来存放x到各个点的最小距离
数组dis,用来存放各个点到x的最小距离

*/
void Dijkstra()
{
	int i,j;
	memset(book,0,sizeof(book));//全部是估计值
	book[x]=1;//x到x的距离最短 
	// 
	for(i=1;i<=n;i++)///最初dis数组中存放的是x直接到任意一个点的最小值,
		dis[i]=e[x][i];/而不是任意两点的最小值 

	int minn,u,v;
	for(i=1;i<=n-1;i++)//为什么这里最多循环为n-1次,那是因为源点到源点本身最短路肯定为0,剩下(n-1)个顶点到源点的距离才是我们要求的,而每一次循环我们一定确定一个顶点的最短路,为什么呢,看下面的解释
	{
		minn=inf;
		u=-1;
	
		for(j=1;j<=n;j++)//寻找离源点到最近的一个点 
		{
			if(!book[j]&&minn>dis[j])//book[j]==1表明dis[j]已经确定过了,是最短的
			{
				minn=dis[j];
				u=j;
			}
		}
		/*
       这里说说为什么找到了离源点最近的顶点u,就一定可以确定u到源点的最短路了,为什么不能再通过别的路使得源点到u的距离变的更短了,首先你要明白,我们每次都是寻找的就是离源点的最近的顶点,所以当然不可能存在比该路更短的路了。你还要明白,此刻的u不一定是和源点直接相连的顶点,有可能是了经过很多顶点才到达顶点u的,但无论经过多少顶点到达u的,这条源点到u的路一定是最短的,可以相当于贪心,每次都是寻找最短,最短从而得到最短
        */
		if(u==-1)
			break;
		/*
		这里为什么要加一个跳出循环的操作呢,那是因为,这里的(n-1)次循环我们其实假设的是源点一定可以到其它(n-1)个顶点(无论是直接到达,还是间接到达)。但有些样例可能存在源点永远不可能到达的顶点,假设只有n-5个顶点与源点相连,那么其实我们只循环n-5次就可以了,所以这里才会加一个提前跳出的操作
		*/
		book[u]=1;//dis[u]最短,不可能再小了
		for(v=1;v<=n;v++)//寻找与u相连的顶点v,利用顶点u,看是否可以使源点到v的距离更短,即能否使dis[v]更小
		{
			if(!(book[v]||e[u][v]<inf))//表明u与v有路,dis[v]有可能变小,或者,若book[v]==0,dis[v]也有可能变小。
			{
				if(dis[v]>dis[u]+e[u][v])//是否可使dis[v]更小
					dis[v]=dis[u]+e[u][v];
			}
		}
	}
}
int main()
{
	int i,j,u,v,w,t;
	while(~scanf("%d %d %d",&n,&m,&x))
	{
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
				if(i==j)	e[i][j]=0;
				else 		e[i][j]=inf;
		for(i=1;i<=m;i++)
		{
			scanf("%d %d %d",&u,&v,&w);
			e[u][v]=min(w,e[u][v]);//单向边,可能有重边 
		}
		Dijkstra();
	
		for(i=1;i<=n;i++)
			dis1[i]=dis[i];
	
		/*
		源点x到别的点的最短路已求出,
	
		因为是单向边,因此二者往返很可能不是一条路
		那该怎么求别的点到源点x的最短路
	
		只需将给出的边方向翻转再使用Dijkstra(x),仔细想想,因为原本可以走的路现在因为边翻转的缘故,已经不能走了,x若还想到达其它点,其实走的路便是其它点到x所走的路 
	
		*/ 
	
		for(i=1;i<=n;i++)
		{
			for(j=i+1;j<=n;j++)
			{
				t=e[i][j];//边翻转,不能覆盖 
				e[i][j]=e[j][i];
				e[j][i]=t;
			}
		}
		
		Dijkstra();
	
		int maxx=-inf;
		for(i=1;i<=n;i++)
			if(dis[i]<inf&&dis1[i]<inf)
				maxx=max(maxx,dis[i]+dis1[i]);
		printf("%d\n",maxx);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Helinshan/article/details/109919967