[poj3259]Wormholes(spfa判负环)
题意:有向图判负环。
解题关键:spfa算法+hash判负圈。
spfa判断负环:若一个点入队次数大于节点数,则存在负环。
两点间如果有最短路,那么每个结点最多经过一次,这条路不超过$n-1$条边。”
如果一个结点经过了两次,那么我们走了一个圈。如果这个圈的权为正,显然不划算;如果是负圈,那么最短路不存在;如果是零圈,去掉不影响最优值。
也就是说,每个点最多入队$n-1$次,可以想象一下,左边$n-1$个节点全部指向右边一个节点,遍历的顺序恰好与边权顺序相反。
负圈是指圈上的总和小于0
实际只要大于$n-1$次,即可判断。上述判断条件仅为充分条件。
1至于为什么是$>n$是因为对于一个节点时候 如果还是$>n-1$那么任意一个单节点的图会被判定为存在负环 综合考虑取$>n$
入队vis=true,出队vis=false
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<iostream> #include<cmath> #include<queue> using namespace std; const int inf=0x3f3f3f3f; const int maxm=11111; const int maxn=500; int head[maxn],tot,n,m,num[maxn],w; struct edge{ int to; int w; int nxt; }e[maxm]; void add_edge(int u,int v,int w){ e[tot].w=w; e[tot].to=v; e[tot].nxt=head[u]; head[u]=tot++; } bool vis[maxn]; queue<int>que;//队列是点的队列 int d[maxn]; bool spfa(int s){ memset(num,0,sizeof num); fill(d+1,d+n+1,inf); memset(vis,0,sizeof vis); while(!que.empty()) que.pop(); que.push(s); vis[s]=true; d[s]=0; while (!que.empty()){ int u=que.front(); que.pop(); vis[u]=false; for (int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; int w=e[i].w; if (d[v]>d[u]+w){ d[v]=d[u]+w; if (!vis[v]){ vis[v]=true; que.push(v);//hash一下,可判断是否存在负环 num[v]++; if(num[v]>n) return true; } } } } return false; } int main(){ ios::sync_with_stdio(0); int a,b,c,T; cin>>T; while(T--){ tot=0; memset(head,-1,sizeof head); cin>>n>>m>>w; for(int i=0;i<m;i++){//注意为双向边 cin>>a>>b>>c; add_edge(a,b,c); add_edge(b,a,c); } for(int i=0;i<w;i++){ cin>>a>>b>>c; add_edge(a,b,-c); } if(spfa()) puts("YES"); else puts("NO"); } return 0; }