bellman算法相对dijkstra能对负环进行判断,dijkstra因为算法应用了贪心思路,会对走过的点进行标记不走回头路,所以面对负环时会无法算出最短值如A->B为2,A->C为1,C->B为-1,A到B最短值为0,但因为dijkstra会先从A到B最后结果为2与实际不符,这时人们发明了bellman算法。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=5e5+7;
int mod=100003;
LL vis[N];
LL d[N];//记录用n条边能
struct Edge{
int to,next,val;
}edge[N];
int head[N];
int cnt=0;
void addedge(int b,int e,int val)
{
cnt++;
edge[cnt].to=e;
edge[cnt].next=head[b];
edge[cnt].val=val;
head[b]=cnt;
} int n,m,s;
//dp思路,每一次从上一次做完的继承下来,每n次代表走n次能到点的最大值
bool bellman_ford(int x)
{
for(int i=1;i<=n;i++) d[i]=pow(2,31)-1; d[x]=0;
bool flag;
for(int i=1;i<=n;i++)//i是次数,做了几次
{
flag=false;
for(int k=1;k<=n;k++)
{
for(int j=head[k];j;j=edge[j].next)
{//遍历每一个点
if(d[edge[j].to]>edge[j].val+d[k])//松驰
{
d[edge[j].to]=edge[j].val+d[k];
flag=true;
}
}
}
if(!flag)break;
}
if(flag) return false;
else return true;
}
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
addedge(u,v,w);
}
bellman_ford(s);
for(int i=1;i<=n;i++)cout<<d[i]<<" ";
return 0;
}
bellman算法的思路是重复n次,每次便利n条边最后求的最短值。
这里挂个链接方便理解贝尔曼福特算法说人话版(Bellman-Ford)_哔哩哔哩_bilibili
同时在视频中我们发现已近确定的边依然会被遍历,在bellman算法下我们可以当便利所有边是最小值都不会改变是break,来减少复杂度。同时我们也可以利用spfa算法来进行优化。
#include<iostream>
#include<bits/stdc++.h>
#include<fstream>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
struct E
{
int to,v;
}e;
vector<E> G[20001];
long long n,m,I,cost[10001];
bool visit[10005];
bool SPFA()
{
long h=0,t=1;
queue<int> q;
for(long i=1;i<=n;i++) cost[i]=0x3f3f3f3f;
cost[I]=0;
q.push(I);
memset(visit,0,sizeof(visit));
while(!q.empty())
{
int t=q.front();
for(int i=0; i<G[t].size(); i++)
if(cost[G[t][i].to]> cost[t]+ G[t][i].v)
{
cost[G[t][i].to]=cost[t]+ G[t][i].v;
if (!visit[G[t][i].to])
{
q.push(G[t][i].to);
visit[G[t][i].to]=1;
}
}
visit[t]=0;
q.pop();
}
return true;
}
void init()
{
cin>>n>>m>>I;
int x;
for(int i=1;i<=m;i++)
{
cin>>x>>e.to>>e.v;
G[x].push_back(e);
}
SPFA();
for(int i=1;i<=n;i++)
if(cost[i]==0x3f3f3f3f)cout<<"2147483647 ";
else cout<<cost[i]<<' ';
}
int main()
{
init();
return 0;
}
spfa算法利用一个队列(不是dijkstra的优先队列)对要走的边加入队列,已近确定的不再重复计算提高运行效率。需要注意的是是spfa的标记在每次取出是要去除,标记是为了减少不必要的运算,去除是为了避免成为dijkstra。(这段代码我把求负环的删了)
(这篇文章是对我学习的总结,如有问提我会虚心求教,希望大家的指正)