D03【模板】最短路 Bellman-Ford 算法 SPFA 算法 P3385 负环
D03 最短路 Bellman-Ford 算法 SPFA 算法——信息学奥赛算法_哔哩哔哩_bilibili
队列优化:SPFA
即 Shortest Path Faster Algorithm.
很多时候我们并不需要那么多无用的松弛操作.
只有上一次被松弛的结点,所连接的边,才有可能引起下一次的松弛操作.
那么我们用队列来维护「哪些结点可能会引起松弛操作」,就能只访问必要的边了.
SPFA 也可以用于判断 𝑠
点是否能抵达一个负环,只需记录最短路经过了多少条边,当经过了至少 𝑛
条边时,说明 𝑠
点可以抵达一个负环.
虽然在大多数情况下 SPFA 跑得很快,但其最坏情况下的时间复杂度为 𝑂(𝑛𝑚)
,将其卡到这个复杂度也是不难的,所以考试时要谨慎使用
(在没有负权边时最好使用 Dijkstra 算法,在有负权边且题目中的图没有特殊性质时,若 SPFA 是标算的一部分,题目不应当给出 Bellman–Ford 算法无法通过的数据范围).
// spfa算法 O(m~nm) #include<bits/stdc++.h> using namespace std; const int N=10010,M=500010,inf=(1<<31)-1; int n,m,s,a,b,c; int h[N],to[M],w[M],ne[M],tot; void add(int a,int b,int c){ to[++tot]=b;w[tot]=c;ne[tot]=h[a];h[a]=tot; } int d[N],vis[N]; void spfa(){ for(int i=1;i<=n;i++) d[i]=inf; d[s]=0; queue<int>q; q.push(s); vis[s]=1; //标记在队中 while(q.size()){ int u=q.front(); q.pop(); vis[u]=0; //标记不在队中 for(int i=h[u];i;i=ne[i]){ int v=to[i]; if(d[v]>d[u]+w[i]){ //松弛 d[v]=d[u]+w[i]; if(!vis[v]) q.push(v),vis[v]=1; //不在队中才入队 } } } } int main(){ cin>>n>>m>>s; for(int i=0;i<m;i++){ scanf("%d%d%d",&a,&b,&c); add(a,b,c); } spfa(); for(int i=1;i<=n;i++) printf("%d ",d[i]); }
//spfa 判负环 530ms #include<bits/stdc++.h> using namespace std; const int N=2010,M=6010; int n,m; int h[N],to[M],ne[M],w[M],tot; int d[N],vis[N],cnt[N]; void add(int a,int b,int c){ to[++tot]=b;w[tot]=c;ne[tot]=h[a];h[a]=tot; } bool spfa(){ //判负环 memset(d,0x3f,sizeof d); d[1]=0; memset(vis,0,sizeof vis); memset(cnt,0,sizeof cnt); queue<int>q; q.push(1); vis[1]=1; //在队中 while(q.size()){ int u=q.front();q.pop();vis[u]=0; for(int i=h[u];i;i=ne[i]){ int v=to[i]; if(d[v]>d[u]+w[i]){ //松弛 d[v]=d[u]+w[i]; if(!vis[v]) q.push(v),vis[v]=1; cnt[v]=cnt[u]+1; //边数 if(cnt[v]>=n) return 1; //有负环 } } } return 0; } int main(){ int T; scanf("%d",&T); while(T--){ tot=0; memset(h,0,sizeof(h)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); if(w>=0)add(v,u,w);; } puts(spfa()?"YES":"NO"); } }
//spfa 判负环 690ms #include<bits/stdc++.h> using namespace std; const int inf=0x3f3f3f3f; const int N=2010,M=6010; int n,m; int to[M],ne[M],w[M],h[N],tot; int d[N],cnt[N],vis[N]; void add(int a,int b,int c){ to[++tot]=b;w[tot]=c; ne[tot]=h[a];h[a]=tot; } bool spfa(){ //判负环 memset(d,0x3f,sizeof d); memset(vis,0,sizeof vis); memset(cnt,0,sizeof cnt); queue<int>q; q.push(1); vis[1]=1; d[1]=0; while(q.size()){ int u=q.front();q.pop();vis[u]=0; for(int i=h[u];i;i=ne[i]){ int v=to[i]; if(d[v]>d[u]+w[i]){ d[v]=d[u]+w[i]; if(++cnt[v]>n)return 1;//判点数 if(!vis[v])q.push(v),vis[v]=1; } } } return 0; } int main(){ int T; scanf("%d",&T); while(T--){ tot=0; memset(h,0,sizeof(h)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); if(w>=0)add(v,u,w);; } puts(spfa()?"YES":"NO"); } return 0; }
//Ford 判负环 740ms #include<bits/stdc++.h> using namespace std; const int inf=0x3f3f3f3f; const int N=2010,M=6010; int n,m; int h[N],to[M],w[M],ne[M],tot; int d[N]; void add(int a,int b,int c){ to[++tot]=b;w[tot]=c; ne[tot]=h[a];h[a]=tot; } bool ford(){ memset(d,inf,sizeof d); d[1]=0; bool flag; //是否松弛 for(int i=1;i<=n;i++){ //跑n轮 flag=false; for(int u=1;u<=n;u++){ //n个点 if(d[u]==inf)continue; for(int j=h[u];j;j=ne[j]){ int v=to[j]; if(d[v]>d[u]+w[j]){ d[v]=d[u]+w[j]; flag=true; } } } if(!flag)break; } return flag; //第n轮=true,有负环 } int main(){ int T; scanf("%d",&T); while(T--){ tot=0; memset(h,0,sizeof(h)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); if(w>=0)add(v,u,w);; } puts(ford()?"YES":"NO"); } }
浙公网安备 33010602011771号