• 【原创】POJ 3259 Wormholes(Bellman-Ford) && 简介Bellman-Ford算法


    【原创】

    题目大意

    John有N个农场,一共有M条边,在农场上出现了W个虫洞(W是一条边),其中M是双向普通边,W是单向虫洞边。John穿行于农场之间每经过一条边(S到E)的时间为+T,每经过虫洞会时间倒流,经过-T。问John会不会在某一刻看到以前的自己。这个题目即问的是,存不存在负权环。bollman_ford

    先粘一下百度百科的话:

    Bellman - ford算法是求含负权图的单源最短路径算法,效率很低,但代码很容易写。其原理为持续地进行松弛(原文是这么写的,为什么要叫松弛,争议很大),在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。Bellman - ford算法有一个小优化:每次松弛先设一个标识flag,初值为FALSE,若有边更新则赋值为TRUE,最终如果还是FALSE则直接成功退出。Bellman-ford算法浪费了许多时间去做没有必要的松弛,而SPFA算法用队列进行了优化,效果十分显著,高效难以想象。SPFA还有SLF,LLL,滚动数组等优化。

    首先指出,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|v|-1条边。
    其次,从源点s可达的所有顶点如果 存在最短路径,则这些最短路径构成一个以s为根的最短路径树。Bellman-Ford算法的迭代松弛操作,实际上就是按顶点距离s的层次,逐层生成这棵最短路径树的过程。
    在对每条边进行第1遍松弛的时候,生成了从s出发,层次至多为1的那些树枝。也就是说,找到了与s至多有1条边相联的那些顶点的最短路径;对每条边进行第2遍松弛的时候,生成了第2层次的树枝,就是说找到了经过2条边相连的那些顶点的最短路径……。因为最短路径最多只包含|v|-1 条边,所以,只需要循环|v|-1 次。
    每实施一次松弛操作,最短路径树上就会有一层顶点达到其最短距离,此后这层顶点的最短距离值就会一直保持不变,不再受后续松弛操作的影响。(但是,每次还要判断松弛,这里浪费了大量的时间,怎么优化?单纯的优化是否可行?)
    注意:上述只对正权图有效。如果存在负权不一定第i次就能确定最短路,且与边的顺序有关。
    如果没有负权回路,由于最短路径树的高度最多只能是|v|-1,所以最多经过|v|-1遍松弛操作后,所有从s可达的顶点必将求出最短距离。如果 d[v]仍保持 +∞,则表明从s到v不可达。
    如果有负权回路,那么第 |v| 遍松弛操作仍然会成功,这时,负权回路上的顶点不会收敛。

    这其他的不多说,先说一下松弛计算,其实就是更新距离嘛,这种思想在运筹学好像称之为松弛,我们知道,假如图中的边全部为正权边就一定会有最短路径,假如存在负权环,那么每走一圈,起点的权值就会比原来小,那么你走无数圈,你的权值就会无限减小。那么百度百科的意思就是,你先做标准次数的距离更新(理论上讲已经无法再更新了),假如你再一次更新距离,如果还能更新,就一定存在负权环咯。

     1 /*题目大意
     2   John有N个农场,一共有M条边,在农场上出现了W个虫洞(W是一条边),其中M是双向普通边
     3   ,W是单向虫洞边。John穿行于农场之间每经过一条边(S到E)的时间为+T,每经过虫洞会时
     4   间倒流,经过-T。问John会不会在某一刻看到以前的自己。
     5   这个题目即问的是,存不存在负权环。bollman_ford
     6 */
     7 
     8 #include<cstdio>
     9 #include<iostream>
    10 #include<memory.h>
    11 #define max 9999999
    12 using namespace std;
    13 int n,m,w,top;
    14 typedef struct
    15 {
    16     int x,y,t;
    17 } e;
    18 e edge[6000];
    19 
    20 bool bellman_ford(int en)//en = top-1,即边的数量
    21 {
    22     int dis[520],u,v,w;
    23     for(int i=1;i<=n;i++) dis[i] = max;
    24     dis[1] = 0;
    25     for(int i=0;i<n-1;i++)    //百科说:首先指出,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|v|-1条边。
    26     {
    27         //for(int k=1;k<=n;k++)   printf("%d ",dis[k]);
    28         for(int j = 1;j <= en;j++)
    29         {
    30             u = edge[j].x;
    31             v = edge[j].y;
    32             w = edge[j].t;
    33             if(dis[u]<max && dis[u]+w < dis[v])
    34             {
    35                 dis[v] = dis[u]+w;
    36             }
    37         }
    38     }
    39     for(int i=1;i<=en;i++)
    40     {
    41         u = edge[i].x;
    42         v = edge[i].y;
    43         w = edge[i].t;
    44         if(dis[u]<max && dis[u]+w<dis[v])
    45         {
    46             return true;//极限更新n-1,我们再更新en次,如果还能再更新,那返回TRUE表示存在负权环
    47         }
    48     }
    49     return false;//不能再更新了,返回FALSE
    50 }
    51 int main(){
    52     int T;
    53     scanf("%d", &T);
    54     while(T--){
    55         top = 1;
    56         scanf("%d %d %d", &n, &m, &w);
    57         for(int i=0; i<m; i++){
    58             int a,b,c;
    59             scanf("%d %d %d", &a, &b, &c);
    60             edge[top].x = a;
    61             edge[top].y = b;
    62             edge[top].t = c;
    63             top++;
    64             edge[top].x = b; //这里一定要注意,普通的边是双向的,所以要重新反过来存一次
    65             edge[top].y = a;
    66             edge[top].t = c;
    67             top++;
    68         }
    69         for(int i=0; i<w; i++){
    70             int a,b,c;
    71             scanf("%d %d %d", &a, &b, &c);
    72             edge[top].x = a;
    73             edge[top].y = b;
    74             edge[top].t = -c;  //虫洞是负的且是单向所以  取负  只用存一次
    75             top++;
    76         }
    77         if(bellman_ford(top-1)) printf("YES
    ");
    78         else printf("NO
    ");
    79     }
    80 }

     

  • 相关阅读:
    多线程学习
    Redis学习2
    Redis学习1
    封装
    创建对象内存分析
    稀疏数组
    反转数组、冒泡排序
    可变参数
    .net core 3.x Web Api + Docker个人练手项目
    .net 性能优化手段
  • 原文地址:https://www.cnblogs.com/liwenchi/p/5684484.html
Copyright © 2020-2023  润新知