为什么Dijkstra
算法不能使用在含负权的图中?对Bellman—ford
算法的介绍等,下面这篇题解讲得很不错。
AcWing 853. 有边数限制的最短路 - AcWing
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510;//点
const int M = 10010;//边
//用结构体存储边
struct Edge
{
int a,b,c;
}edges[M];
int n,m,k;
int dist[N];
int last[N];//每次迭代存储的dist数组备份
void bellman_ford()
{
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
//不超过k条边我们就设定循环最大值为k
for(int i=0;i<k;i++)
{
//每次迭代我们需要备份一次dist数组,目的是保证每次迭代只更新长度为1
//不会出现更新了一个点,又用一个点去更新别的点,串联不已
memcpy(last,dist,sizeof dist);
//枚举edges里面存的所有边,可以更新的,我们就更新
for(int j = 0;j<m;j++)
{
auto e = edges[j];
//注意这里一定要用last数组去更新
dist[e.b] = min(dist[e.b],last[e.a]+e.c);
}
}
}
int main()
{
cin>>n>>m>>k;
for(int i = 0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
edges[i] = {a,b,c};
}
bellman_ford();
if(dist[n]>0x3f3f3f3f/2)puts("impossible");
else cout<<dist[n]<<endl;
return 0;
}
我就在这里补充我自己当时疑惑的一个点吧。
对于memcpy(last,dist,sizeof dist);
这一行,我们为什么在每次迭代刚开始时,需要备份一下当前状态的dist
数组呢,我们要知道,每次迭代,我们用已经确定了dist数组的点(也就dist数组不再是0x3f3f3f3f)的点,同时去向外延伸一个单位的长度去更新其他的点。这里有两个很关键的词,同时 和 一个单位,设想如果我们不去备份,我用A点去更新了距离一个单位的B点,我之后又可以用这个B点去更新距离B点一个单位的点,穷穷不尽,这样我在一次迭代之中,就更新了长度远远不止1的一条路径,就和我们写的这个for循环:
//不超过k条边我们就设定循环最大值为k
for(int i=0;i<k;i++)
就相互冲突了,我们的目的是想做到每次更新,都只更新长度为1的边。显然如果我们备份一下dist数组,我们每次的更新,就只会用在每一轮迭代开始时候已经确定dist的点去更新,且只更新一个长度,不会出现我在这一轮更新出来的新的点,在我们这一轮更新也会被用上去更新别的点,这就造成了一种我们不希望的串联,我们希望这一轮迭代更新出来的点,只是在下一轮更新的时候才开始投入使用。
本人讲得肯定不够浅显,不懂的童鞋们可以去看一下y总的视频讲解:
AcWing 853. 有边数限制的最短路 - AcWing 传送点在16:38处。