POJ3259---Wormholes(最短路:验证存在负环)

来源:互联网 发布:大话数据结构知乎 编辑:程序博客网 时间:2024/05/29 08:22

题目来源:http://poj.org/problem?id=3259
【题意】
农夫有n块地,这n块地有m条路径,具有相应的时间(走这条路),同时也有w个时间 虫洞,可以从一块地到另一块地,并且减少相应的时间,问:是否能够遇到之前的自己。。。
【思路】
m条路径是双向的,而w个虫洞是单向的。而这道题也就变成了判断是否存在负数环,因为一旦有了负数环,时间会不停地减少,肯定会遇见曾经的自己的。
验证是否存在负环的方法有两种:
1、用bellman_ford算法。经验证:所有的边重复松弛n-1次就可以得到最短路。那么如果松弛n-1次后依旧可以松弛,那么可证存在负环。(验证:bellman-ford算法的基本思想是,对图中除了源顶点s外的任意顶点u,依次构造从s到u的最短路径长度序列dist[u],dis2[u]……dis(n-1)[u],其中n是图G的顶点数,dis1[u]是从s到u的只经过1条边的最短路径长度,dis2[u]是从s到u的最多经过G中2条边的最短路径长度……当图G中没有从源可达的负权图时,从s到u的最短路径上最多有n-1条边。因此,
dist(n-1)[u]就是从s到u的最短路径长度(推荐博客:算法复习 – 图论 最短路和最小生成树))
2、用spfa算法。经验证:当一个点重复进入队列n次以上,就存在负环。
【代码】

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int INF=0x3f3f3f3f;int n,m,w,k;struct p{    int u,v;    int t;}edge[5210];int d[510];bool bellman(){    for(int i=2;i<=n;i++)    {        d[i]=INF;    }    d[1]=0;    for(int i=1;i<n;i++)//重复n-1次    {        bool flag=1;//是否存在负环        for(int j=0;j<k;j++)//所有的边        {            if(d[edge[j].u]>d[edge[j].v]+edge[j].t)            {                 d[edge[j].u]=d[edge[j].v]+edge[j].t;                 flag=0;            }        }        if(flag) return 0;//若是当前这一轮没有松弛,那么一定不存在负环。    }    for(int i=0;i<k;i++)    {        if(d[edge[i].u]>d[edge[i].v]+edge[i].t)            return 1;    }    return 0;}int main(){    int T;    scanf("%d",&T);    while(T--)    {        k=0;        scanf("%d%d%d",&n,&m,&w);        for(int i=1;i<=m;i++)//双向        {            int u,v,t;            scanf("%d%d%d",&u,&v,&t);            edge[k].u=u;            edge[k].v=v;            edge[k++].t=t;            edge[k].v=u;            edge[k].u=v;            edge[k++].t=t;        }        for(int i=1;i<=w;i++)//单向        {            int u,v,t;            scanf("%d%d%d",&u,&v,&t);            edge[k].u=u;            edge[k].v=v;            edge[k++].t=-t;//时间减少        }        if(bellman())            printf("YES\n");        else            printf("NO\n");    }}