这道题有负权边,本来考虑可以用spfa,但是这个算法被卡了,因此只能转换思路。
我们发现因为负权边是单向的,且没有环,而正权是有环的,这说明这个图是一块一块的,负权边就是联通块和块的,因此这构成一个DAG
我们考虑在块内部使用迪杰斯特拉算法而在块和块之间使用拓扑排序来做
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<vector> #define x first #define y second using namespace std; typedef pair<int,int> pll; const int N=1e5+10; const int inf=0x3f3f3f3f; vector<int> g[N]; int id[N],e[N],ne[N],w[N],h[N],idx; bool st[N]; int t,r,p,s; int bcnt; int in[N]; queue<int> q; int dis[N]; void add(int a,int b,int c){ e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++; } void dfs(int x,int bcnt){ id[x]=bcnt; g[bcnt].push_back(x); int i; for(i=h[x];i!=-1;i=ne[i]){ int j=e[i]; if(!id[j]) dfs(j,bcnt); } } void dij(int t){ priority_queue<pll,vector<pll>,greater<pll> > pq; for(auto x:g[t]){ pq.push({dis[x],x});// all in beacause it will contribute to the next block } while(pq.size()){ auto u=pq.top(); pq.pop(); int ver=u.y,distance=u.x; if(st[ver]) continue; st[ver]=1; int i; for(i=h[ver];i!=-1;i=ne[i]){ int j=e[i]; if(dis[j]>dis[ver]+w[i]){ dis[j]=dis[ver]+w[i]; if(id[j]==id[ver]){ pq.push({dis[j],j}); } } if(id[j]!=id[ver]&&--in[id[j]]==0) q.push(id[j]); } } } void topo(int x){ int i; memset(dis,0x3f,sizeof dis); dis[x]=0; for(i=1;i<=bcnt;i++){ if(!in[i]){ q.push(i); } } while(q.size()){ int t=q.front(); q.pop(); dij(t); } } int main(){ cin>>t>>r>>p>>s; int i; memset(h,-1,sizeof h); for(i=1;i<=r;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } for(i=1;i<=t;i++){ if(!id[i]){ bcnt++; dfs(i,bcnt); } } for(i=1;i<=p;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); in[id[b]]++; } topo(s); for(i=1;i<=t;i++){ if(dis[i]>inf/2) cout<<"NO PATH"<<endl; else cout<<dis[i]<<endl; } }