poj 3259 Wormholes

/*
题意:John的农场里n块地,M条路连接两块地,是双向边,
W个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退w秒。
问能否在从某块地出发后又回来,看到了离开之前的自己,即求负权回路
思路:Bellman-Ford算法,源点不明确,求负权回路


当源点不明确时:
与SPFA算法不同,当源点不明确时,Bellman-Ford算法不用添加超源点,
因为它对图中每一条边都会做n-1次松弛,就保证每个顶点都得到更新。
对于此题,如何控制初值,一个比较原始的想法是,枚举起点,这样的时间复杂度大约为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之间是单向的,
即只有负权路径是单向的,这种情况下只要图是连通的,任何一个点作为起点都可以!


*/

#include <iostream> //Bellman-Ford算法
using namespace std;
#define inf 99999
struct Edge
{
int u, v;
int w;
}edge[10000];
int dist[502],n, m;
int relax(int u, int v, int w)
{
if(dist[v] > dist[u] + w)
{
dist[v] = dist[u] + w;
return 1;
}
return 0;
}
int Bellman_Ford() //若存在负权回路则返回 0
{
for(int i=1; i<=n-1; ++i)
{
int go_on=0;
for(int j=1; j<=m; ++j)
{
if(relax(edge[j].u, edge[j].v, edge[j].w))
go_on=1;
}
if(!go_on)
break;
}
for(int i=1; i<=m; ++i)
{
if(dist[edge[i].v] > dist[edge[i].u] + edge[i].w) //存在负权回路
return 0;
}
return 1;
}
int main()
{
int cases,M,W;
cin>>cases;
while(cases--)
{
cin>>n>>M>>W;
m=2*M+W; //边数
fill(&dist[1],&dist[n+1],0); //没有明确的源点,dist可以初始化为任意数值
int u,v,w;
int j=1;
for(int i=1; i<=M+W;++i)
{
cin>>u>>v>>w;
if(i<=M)
{
//field之间是双向的
edge[j].u=u; edge[j].v=v;
edge[j++].w=w;
edge[j].u=v; edge[j].v=u;
edge[j++].w=w;
}
else
{
edge[j].u=u;edge[j].v=v; //wormholes之间是单向的
edge[j++].w=-w;
}
}
if(Bellman_Ford()) //不存在负权回路
printf("NO\n");
else
printf("YES\n");
}
return 0;
}

posted on 2011-07-22 20:41  sysu_mjc  阅读(162)  评论(0编辑  收藏  举报

导航