題目描述
輸入數據給出一個有N(2 < = N < = 1,000)個節點,M(M < = 100,000)條邊的帶權有向圖. 要求你寫一個程序, 判斷這個有向圖中是否存在負權回路. 如果從一個點沿著某條路徑出發, 又回到了自己, 而且所經過的邊上的權和小於0, 就說這條路是一個負權回路. 如果存在負權回路, 只輸出一行-1; 如果不存在負權回路, 再求出一個點S(1 < = S < = N)到每個點的最短路的長度. 約定: S到S的距離為0, 如果S與這個點不連通, 則輸出NoPath.
輸入
第一行: 點數N(2 < = N < = 1,000), 邊數M(M < = 100,000), 源點S(1 < = S < = N); 以下M行, 每行三個整數a, b, c表示點a, b(1 < = a, b < = N)之間連有一條邊, 權值為c(-1,000,000 < = c < = 1,000,000)
輸出
如果存在負權環, 只輸出一行-1, 否則按以下格式輸出 共N行, 第i行描述S點到點i的最短路: 如果S與i不連通, 輸出NoPath; 如果i = S, 輸出0; 其他情況輸出S到i的最短路的長度.
這個題真是異常的坑 打著題目是sssp的表面而實地裡卻隱藏這一刻spfa的心(貌似不通) 下面講一下spfa的詳細操作步驟(和dijkstra應該很像):
g[i][j]表示鄰接矩陣 dist[i]表示源點到i的距離 cnt[i]表示點i的入隊次數 v[i]表示i這個點是否在隊列中
初始化:v[]數組賦值為false cnt[]=0 把所有點與源點的距離變為很大
接著 把源點入隊 再把dist[start]變為0
然後做和bfs差不多的操作 拓展隊首的點 更新新的最短的距離......
如果某個點的入隊次數>n那麼一定有負環 證明:如果一個點存在正的最短路 那麼他最多可以和其他所有點連而拓展n次 而如果是負環 那麼他的這個最短路中如果有負環 那麼就會越拓展越小 當然入隊就會超過n次
這裡還有一個地方要注意 就是判負環 因為這個負環不一定在源點的路上 那麼是不是應該把所有點都找過去呢 顯然不是 這裡有兩個方法 推薦第二種做法:
用dfs找連通塊 然後對每一個聯通塊做SPFA
受zbt大神的指點 可以加一個入度為0 只有出邊並連著除他外所有點 那麼只要對這個點進行拓展就可以找到所有的負環
最後還有一點就是我這樣做在vijos裡只有50分 粗部估計是這個鄰接矩陣的問題 最好改成邊集數組來做 代碼下次給
代碼如下:
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> using namespace std; const int maxn=1000+10; long long g[maxn][maxn],dist[maxn],cnt[maxn]; bool v[maxn],used[maxn]; int n,m,s; int a,b,c; queue<int>q; bool SPFA(int start) { for(int i=1;i<=n;i++) { dist[i]=0x7f7f7f; cnt[i]=0; v[i]=false; } while(!q.empty()) q.pop(); v[start]=true; q.push(start); dist[start]=0; while(!q.empty()) { int x=q.front(); q.pop(); v[x]=false; for(int k=1;k<=n;k++) if(g[x][k]<0x7f7f7f&&dist[x]+g[x][k]<dist[k]) { dist[k]=dist[x]+g[x][k]; // used[k]=true; if(!v[k]) { cnt[k]++; if(cnt[k]>n) return false; v[k]=true; q.push(k); } } } return true; } int main() { ios::sync_with_stdio(false); // freopen("1.in","r",stdin); cin>>n>>m>>s; for(int i=1;i<=n+1;i++) for(int j=1;j<=n+1;j++) g[i][j]=0x7f7f7f; for(int i=1;i<=m;i++) { cin>>a>>b>>c; if(c<g[a][b]) g[a][b]=c; } for(int i=1;i<=n;i++) g[n+1][i]=1; // for(int i=1;i<=n;i++) // { // for(int j=1;j<=n;j++) // cout<<g[i][j]<<' '; // cout<<endl; // } // for(int i=1;i<=n;i++) // { // if(!SPFA(i)) // { // cout<<-1<<endl; // return 0; // } // } if(!SPFA(n+1)) { cout<<-1<<endl; return false; } SPFA(s); for(int i=1;i<=n;i++) { if(dist[i]==0x7f7f7f) { cout<<"NoPath"<<endl; continue; } cout<<dist[i]<<endl; } return 0; }