题目大意:一个农夫在田里走,田里有一些虫洞,可以回到过去,农夫想试试能不能通过虫洞遇到过去的自己。
转化题意:一张n个节点构成的图,有m条正权边,w条负权边,问是否有负权环。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define INF 10005
using namespace std;
const int maxn=1005,maxw=INF;
int d[maxn];
int n,m,w,num;
struct Edge
{
int u,v;
int t;
}edge[maxw];
bool bellman_ford()
{
for(int k=1;k<=n;k++)
d[k]=INF;//初始化
d[1]=0;//起点为 0
for(int k=1;k<n;k++)
{
bool flag=true;
for(int i=0;i<num;i++)
{
int u=edge[i].u;
int v=edge[i].v;
int t=edge[i].t;
if(d[v]>d[u]+t)//松弛操作
{
d[v]=d[u]+t;
flag=false;//d没有更新,说明松弛结束,没有负权边
}
}
if(flag)
return false;
}
for(int i=0;i<num;i++) { if(d[edge[i].v]>d[edge[i].u]+edge[i].t)
return true;//如果仍然能够松弛则存在负环
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d", &n,&m,&w);
num=0;
int u,v,t;
for(int i=0;i<m;i++) //无向边存两次
{
scanf("%d%d%d", &u,&v,&t);
edge[num].u=u;
edge[num].v=v;
edge[num++].t=t;
edge[num].u=v;
edge[num].v=u;
edge[num++].t=t;
}
for(int i=1;i<=w;i++) //无向负权边
{
scanf("%d%d%d",&u,&v,&t);
edge[num].u=u;
edge[num].v=v;
edge[num++].t=-t;//负权边
}
if(bellman_ford()) printf("YES\n"); //存在负数环
else printf("NO\n");
}
return 0;
}
运用了bellman—ford。
之前做题的时候,总是纠结于用什么方法存储边,今天看到直接用存两次的方法存储无向边。。。感觉之前不知道在学什么