使用条件
Bellman-Ford:存在负权边、可以有负环但必须同时要有最多经过k条边 (边
数限制)
SPFA:存在负权边、没有负环 99.9%的题没有负环
Bellman - ford 算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在 n-1 次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成
spfa算法的实现步骤
1.实现spfa算法需要一个队列q,一个标记数组st[N]用来标记某点是否在队列中。数组dist[N],用 来存储起点到某个点的最短距离。
2.初始化dist数组为正无穷
3.从起点开始枚举每个点的所有子节点,设父节点到子节点的距离为s,父节点到起点的距离为dist[u],子节点到起点的距离为dist[v],如果dist[u]+s<dist[v]
当且仅当上式成立时就更新dist[v],如果v没有在队列中,就将v入队。
👉 解析在这👈
#include<bits/stdc++.h>
using namespace std;
const int N=510,M=10010;
int n,m,k;
int dist[N],backup[N];
struct edge{
int a,b,w;
}e[M];
void bellman_ford(){
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<k;i++){
memcpy(backup,dist,sizeof dist);
for(int j=0;j<m;j++){
int a=e[j].a,b=e[j].b,w=e[j].w;
dist[b]=min(dist[b],backup[a]+w);
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
e[i]={a,b,w};
}
bellman_ford();
if(dist[n]>0x3f3f3f3f/2) puts("impossible");
else cout<<dist[n]<<endl;
return 0;
}
👉 详细解析在这👈
👉bellman_ford与spfa的对比👈
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int h[N],e[N],ne[N],w[N],idx;
int dist[N];
bool st[N];
int n,m;
void add(int a,int b,int c){//将边插入到邻接表中
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void spfa(){
memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int> q;
q.push(1);//用起点去扩展其他点
st[1]=true;//标记队列中已存在该点
while(q.size()){
int t=q.front();
q.pop();
st[t]=false;//t点已经出队
for(int i=h[t];i!=-1;i=ne[i]){//i表示指向的位置
int j=e[i];//j表示i指向位置的顶点值
if(dist[j]>dist[t]+w[i]){ //w[i]表示该位置到它上一点权值
dist[j]=dist[t]+w[i];
if(!st[j]){//如果队列中现在没有该点
q.push(j);
st[j]=true;
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
for(int i=0;i<m;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
add(a,b,w);
}
spfa();
if(dist[n]==0x3f3f3f3f) puts("impossible");
else cout<<dist[n]<<endl;
return 0;
}
👉解析👈
#include<bits/stdc++.h>
using namespace std;
const int N=2010,M=10010;
int h[N],e[M],ne[M],w[M],idx;
int dist[N];
bool st[N];
int cnt[N];
int n,m;
void add(int a,int b,int c){
w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool spfa(){
// d 数组无需初始化,因为我们最终需要求的不是真正的距离
queue<int> q;
for(int i=1;i<=n;i++){//题中没有说明是连通图 不知道负环存在在哪个点上 所以把所有点全部加入到队列中
q.push(i);
st[i]=true;
}
while(q.size()){
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+w[i]){//因为负环的存在,所以即使每个点距离虚拟原点距离为0,
//负权边的存在依然可以更新某个点为更小的负值 即不用初始化为无穷大
dist[j]=dist[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n) return true;
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
return false;
}
int main(){
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
if(spfa()) puts("Yes");
else puts("No");
return 0;
}