廉价最短路径【SPFA】

>Description
图是由一组顶点和一组边组成的。一条边连接两个顶点。例如,图1表示了一个有4个顶点V、5条边的图。图中,每条边e是有方向的,方向从起点到终点,并且每条边都有价值。用整数0,1,…,m-1可以表示一个有m个顶点的图。
在这里插入图片描述
一条路径连接了一个点Vi和另一个点Vj,其方向与经过的一系列边的方向一致。路径的长度是途经边的条数,路径的费用是边价值的总和。对于一个给定的图,你的任务是在所有最短路径中,找出需要最少费用的连接V0和V1的路径。一个需要最少费用的最短路径称之为廉价最短路径。
让我们重新考虑图1,从0到1的最短路径是只含一条边的路径0→1,费用是10。当然,还有更便宜的路:0→2→1和 0→3→1,但是它们比第一条路径长(有2条边)。所以,0→1是廉价最短路径。
看一下另一个例子,图2,它有2条最短路径,其长度是2,路径0→3→1(费用=4)比路径0→2→1(费用=5)花费少。还用另一条路径0→2→3→1(费用=3),虽然便宜但是很长。所以,廉价最短路径是0→3→1。


>Input
输入文件第一行有两个整数m和n,用一个空格隔开,其中,m是顶点数,而n是边数。接下来的n行给出所有的边及其价值,每行有3个整数(相邻两个整数间有一个空格),表示起点,终点和边的价值。顶点最多有100个,编号在0到99之间。边最多有1000条,其价值在0到2^15-1之间。

>Output
输出文件仅有一行包含一个整数,即V0→V1的廉价最短路径的费用。当出现有多个廉价最短路径的情况时,它们的费用是一样的。


>Sample Input
4 5
0 2 2
0 3 2
0 1 10
2 1 2
3 1 2

>Sample Output
10


>解题思路
改编版的最短路,就是加上一个前提,在所有的最短路径中找到一条最少费用的。


>代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct ooo
{
	int to,next,c;
}a[1005];
int n,m,x,y,z,tt,h[105],c[105],f[105],ans,anst,head,tail,st[305];
bool lhq[105];
int main()
{
	ans=anst=f[0];
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		a[++tt].to=y; a[tt].next=h[x]; a[tt].c=z; h[x]=tt; //邻接表
	}
	memset(f,0x7f,sizeof(f)); //f为0~i最短路径中的最小费用
	memset(c,0x7f,sizeof(c)); //c为0~i的最短路径
	st[1]=0; c[0]=0; f[0]=0; tail=1; lhq[0]=1;
	while(head<tail)
	{
		head++;
		for(int i=h[st[head]];i;i=a[i].next)
		{
			if(!lhq[a[i].to]) st[++tail]=a[i].to,lhq[a[i].to]=1; //加入队列
			if(c[st[head]]+1<c[a[i].to]) //如果路径可以更新(更短了)
			{
				c[a[i].to]=c[st[head]]+1;
				f[a[i].to]=f[st[head]]+a[i].c; //直接赋值
			}
			else if(c[st[head]]+1==c[a[i].to]) f[a[i].to]=min(f[a[i].to],f[st[head]]+a[i].c); //如果一样的话要找出其中的最小费用
			if(a[i].to==1&&c[a[i].to]<=anst) //如果到达了1并且为最短路径
			{
				if(c[a[i].to]<anst) anst=c[a[i].to],ans=f[a[i].to];
				else if(c[a[i].to]==anst) ans=min(ans,f[a[i].to]);
			}
		}
	}
	printf("%d",ans);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_43010386/article/details/90729597