题意:
John的农场里N块地,M条路连接两块地,W个虫洞;路是双向的,虫洞是一条单向路,会在你离开之前把你传送到目的地,
就是当你过去的时候时间会倒退Ts。我们的任务是知道会不会在从某块地出发后又回来,看到了离开之前的自己。
简化下,就是看图中有没有负权环。
解题:
bellman_ford和spfa算法均可,但是实现效率bellman_ford更简单,也更容易,虽然spfa算法是bellman_ford的增强版,但是在
判断图中是否存在负权回路的问题上要逊色于bellman_ford算法
bellman:
#include <stdio.h> #define maxN 502 #define maxM 2702 #define inf 1000000000 struct EDGE { int u,v,w; }edge[2 * maxM]; int dis[maxN]; int N, M, W; int edgeNum; void Init() { for (int i = 0; i <= N; ++ i) { dis[i] = inf; } } bool bellMan(void) { bool flag; for (int i = 0; i < N - 1; ++ i) { flag = false; for (int j = 0; j < edgeNum; ++ j) { if (dis[edge[j].v] > dis[edge[j].u] + edge[j].w)//松弛 { dis[edge[j].v] = dis[edge[j].u] + edge[j].w; flag = true; } } if (!flag)//存在负权回路 { break; } } for (int i = 0; i < edgeNum; ++ i) { if(dis[edge[i].v] > dis[edge[i].u] + edge[i].w) return true; } return false; } int main() { int F; scanf("%d", &F); while (F --) { scanf("%d%d%d", &N, &M, &W); edgeNum = 0; Init(); int u,v,w; for (int i = 1; i <= M; ++ i) { scanf("%d%d%d", &u, &v, &w);//正权双向 edge[edgeNum].u = u; edge[edgeNum].v = v; edge[edgeNum].w = w; edgeNum ++; edge[edgeNum].u = v; edge[edgeNum].v = u; edge[edgeNum].w = w; edgeNum ++; } for (int i = 1; i <= W; ++ i) { scanf("%d%d%d", &u, &v, &w); edge[edgeNum].u = u; edge[edgeNum].v = v; edge[edgeNum].w = -w;//负权 edgeNum ++; } if(bellMan()) printf("YES\n"); else printf("NO\n"); } return 0; }
spfa 引用
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define MA 6000 #define INF 0x7FFFFFF using namespace std; int nxt[MA],head[MA],ep; struct E { int v,w; }; E e[MA];/* 存储边的信息 */ int N,M,W; void addEdge(int cu,int cv,int cw) { ep++; e[ep].v=cv; e[ep].w=cw; nxt[ep]=head[cu]; head[cu]=ep; } void init() { memset(head,-1,sizeof(head)); ep=0; scanf("%d%d%d",&N,&M,&W); int cu,cv,cw; while(M--) { scanf("%d%d%d",&cu,&cv,&cw); addEdge(cu,cv,cw); addEdge(cv,cu,cw); } while(W--) { scanf("%d%d%d",&cu,&cv,&cw); addEdge(cu,cv,-cw); } } int spfa() { int cnt[MA],inq[MA],dis[MA]; memset(cnt,0,sizeof(cnt));/* 计算点被走过了多少次 */ memset(inq,0,sizeof(inq));/* 判断该点是否在队列中 */ for(int i=2;i<=N;i++) dis[i]=INF; dis[1]=0; inq[1]=1; cnt[1]=1; queue<int>q; q.push(1); while(!q.empty()) { int u=q.front(); q.pop(); inq[u]=0;/* 将该点出队 */ for(int p=head[u];p!=-1;p=nxt[p]) { if(dis[e[p].v]>dis[u]+e[p].w) { dis[e[p].v]=dis[u]+e[p].w; if(!inq[e[p].v]) { inq[e[p].v]=1;/* 将该点入队 */ /* 如果某一点被走过的次数大于n-1次,证明存在负权回路 */ if(++cnt[e[p].v]>=N) return 1; q.push(e[p].v); } } } } return 0; } int main() { int t; scanf("%d",&t); while(t--) { init(); if(spfa()) printf("YES\n"); else printf("NO\n"); } return 0; }