Bellman-Ford算法之二
//poj 3259 Wormholes,源点不明确
//与spfa算法不同,当源点不明确时,bellman_ford算法不用添加超源点,
//因为它对图中每一条边都为做n-1次松弛,就保证每个顶点都得到更新。
//Bellman-Ford算法,求负权回路
//John的农场里field块地,path条路连接两块地,是双向边,hole个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。
//我们的任务是知道会不会在从某块地出发后又回来,看到了离开之前的自己,即是求是否存在负权回路
#include <iostream>
using namespace std;
const int maxint = 99999;
typedef struct Edge
{
int u, v;
int weight;
}Edge;
Edge edge[10000];
int dist[502],nodenum, edgenum;
int F,N,M,W;
void init()
{
nodenum=N;
edgenum=2*M+W;
memset(dist,0,sizeof(dist)); //赋初值,没有明确的源点
int s,t,p,j=1;
for(int i=1; i<=M+W;++i)
{
cin>>s>>t>>p;
if(i<=M)
{
edge[j].u=s;edge[j].v=t;edge[j++].weight=p; //field之间是双向的
edge[j].u=t;edge[j].v=s;edge[j++].weight=p;
}
else
{
edge[j].u=s;edge[j].v=t;edge[j++].weight=-p; //wormholes之间是单向的
}
}
}
bool relax(int u, int v, int weight)
{
if(dist[v] > dist[u] + weight)
{
dist[v] = dist[u] + weight;
return 1;
}
return 0;
}
bool Bellman_Ford()
{
for(int i=1; i<=nodenum-1; ++i)
{
int tag=1;
for(int j=1; j<=edgenum; ++j)
if(relax(edge[j].u, edge[j].v, edge[j].weight))
tag=0;
if(tag==1)
break;
}
for(int i=1; i<=edgenum; ++i)
if(dist[edge[i].v] > dist[edge[i].u] + edge[i].weight)
return 0;
return 1;
}
int main()
{
cin>>F;
while(F--)
{
cin>>N>>M>>W;
init();
if(Bellman_Ford())
printf("NO\n");
else
printf("YES\n");
}
return 0;
}
//对于此题,如何控制初值,一个比较原始的想法是,枚举起点,这样的时间复杂度大约为O(n^4),但是还是可以过...
//而经过观察发现,假设有一个原点0,到所有点的距离都是0,那么我们只需要从这个原点出发去判断图中可能出现的负环,
//因此我们只需要置初值dis[1..n]全部为0(任意相同值均可),做一次bellman_ford即可,
//而有不少的程序默认从1出发,
//首先,一个不需要确定起始点判断负权回路的方法是把dis[1..n]全部置为0,
//然后做bellman_ford,该方法实际上是以负权路径的端点作为bellman_ford算法的起始点。
//这种方法可以处理非连通图。
//其次,spfa和bellman_ford默认1为起点是对的,
//因为field之间的path是双向的,只有wormholes之间是单向的,
//即只有负权路径是单向的,这种情况下只要图是连通的,任何一个点作为起点都可以!