题意:
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;
}