zoukankan      html  css  js  c++  java
  • kuangbin专题四 F 题 负权回路(POJ 3259 Wormholes)

    POJ 3259 Wormholes

    While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way path that delivers you to its destination at a time that is BEFORE you entered the wormhole! Each of FJ's farms comprises N (1 ≤ N ≤ 500) fields conveniently numbered 1..N, M (1 ≤ M ≤ 2500) paths, and W (1 ≤ W ≤ 200) wormholes.

    As FJ is an avid time-traveling fan, he wants to do the following: start at some field, travel through some paths and wormholes, and return to the starting field a time before his initial departure. Perhaps he will be able to meet himself :) .

    To help FJ find out whether this is possible or not, he will supply you with complete maps to F (1 ≤ F ≤ 5) of his farms. No paths will take longer than 10,000 seconds to travel and no wormhole can bring FJ back in time by more than 10,000 seconds.

    Input
    Line 1: A single integer, F. F farm descriptions follow.
    Line 1 of each farm: Three space-separated integers respectively: N, M, and W
    Lines 2.. M+1 of each farm: Three space-separated numbers ( S, E, T) that describe, respectively: a bidirectional path between S and E that requires T seconds to traverse. Two fields might be connected by more than one path.
    Lines M+2.. M+ W+1 of each farm: Three space-separated numbers ( S, E, T) that describe, respectively: A one way path from S to E that also moves the traveler back T seconds.
    Output
    Lines 1.. F: For each farm, output "YES" if FJ can achieve his goal, otherwise output "NO" (do not include the quotes).
    Sample Input
    2
    3 3 1
    1 2 2
    1 3 4
    2 3 1
    3 1 3
    3 2 1
    1 2 3
    2 3 4
    3 1 8
    Sample Output
    NO
    YES
    Hint
    For farm 1, FJ cannot travel back in time.
    For farm 2, FJ could travel back in time by the cycle 1->2->3->1, arriving back at his starting location 1 second before he leaves. He could start from anywhere on the cycle to accomplish this.
     
    题意 :

    POJ 3259

    农夫约翰在探索他的许多农场,发现了一些惊人的虫洞。虫洞是很奇特的,因为它是一个单向通道,可让你进入虫洞的前达到目的地!他的N(1≤N≤500)个农场被编号为1..N,之间有M(1≤M≤2500)条路径,W(1≤W≤200)个虫洞。FJ作为一个狂热的时间旅行的爱好者,他要做到以下几点:开始在一个区域,通过一些路径和虫洞旅行,他要回到最开时出发的那个区域出发前的时间。也许他就能遇到自己了:)。为了帮助FJ找出这是否是可以或不可以,他会为你提供F个农场的完整的映射到(1≤F≤5)。所有的路径所花时间都不大于10000秒,所有的虫洞都不大于万秒的时间回溯。

    输入

    第1行:一个整数F表示接下来会有F个农场说明。

    每个农场第一行:分别是三个空格隔开的整数:N,M和W

    第2行到M+1行:三个空格分开的数字(S,E,T)描述,分别为:需要T秒走过S和E之间的双向路径。两个区域可能由一个以上的路径来连接。

    第M +2到M+ W+1行:三个空格分开的数字(S,E,T)描述虫洞,描述单向路径,S到E且回溯T秒。

    思路:判断是否存在负权回路,存在输出 YES 否则输出 NO

    题意是问是否能通过虫洞回到过去;

    虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。

    我们把虫洞看成是一条负权路,问题就转化成求一个图中是否存在负权回路;

    1.bellman_ford算法

    Bellman-Ford算法流程分为三个阶段:

    (1)初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;

    (2)迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)

    (3)检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点

    v的最短距离保存在 d[v]中。

    2.spfa算法

    我们都知道spfa算法是对bellman算法的优化,那么如何用spfa算法来判断负权回路呢?我们考虑一个节点入队的条件是什么,只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,用一个先进先出的队列来存放被成功松弛的顶点。同样,我们有这样的定理:“两点间如果有最短路,那么每个结点最多经过一次。也就是说,这条路不超过n-1条边。”(如果一个结点经过了两次,那么我们走了一个圈。如果这个圈的权为正,显然不划算;如果是负圈,那么最短路不存在;如果是零圈,去掉不影响最优值)。也就是说,每个点最多入队n-1次(这里比较难理解,需要仔细体会,n-1只是一种最坏情况,实际中,这样会很大程度上影响程序的效率)。

    有了上面的基础,思路就很显然了,加开一个数组记录每个点入队的次数(num),然后,判断当前入队的点的入队次数,如果大于n-1,则说明存在负权回路。

     
    1.floyd实现
    /*
    Source Code
    Problem: 3259        User: 201616040106
    Memory: 1384K        Time: 1688MS
    Language: C++        Result: Accepted
    */
    
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std ;
    
    #define maxn 600
    #define inf 99999999
    int mapp[maxn][maxn] ;
    
    int F ;
    int n , m , w ;
    int s , e , t ;
    
    bool floyd(){
        int k , i , j  , T;
        for(k=1 ; k<=n ; k++){
            for(i=1 ;  i<= n ; i++){
                for(j=1 ; j<=n ; j++){
                    T = mapp[i][k] + mapp[k][j] ;
                    if(mapp[i][j] > T )
                        mapp[i][j] = T ;
                }
                //
                if(mapp[i][i] < 0 ){
                    return true ;
                }
    
            }
        }
        return false ;
    }
    
    int main(){
    
        while(~scanf("%d" , &F)) {
    
            while(F--){
                scanf("%d%d%d" , &n , &m , &w) ;
                // 初始化 mapp数组
                for(int i=1 ; i<= n ; i++){
                    for(int j=1 ; j<=n ; j++){
                        if(i == j ) mapp[i][j] = 0 ;
                        else mapp[i][j] = inf ;
                    }
                }
                // 地图 两点可以有多条双向边
                for(int i=1 ; i<=m ; i++){
                    scanf("%d%d%d" , &s , &e , &t) ;
                    if(t < mapp[s][e] )
                        mapp[s][e] = mapp[e][s] = t ;
                }
                // 虫洞
                for(int i=1 ; i<= w ; i++){
                    scanf("%d%d%d" , &s , &e , &t) ;
                    mapp[s][e] = -t ;
                }
    
                if(floyd()){
                    printf("YES
    ") ;
                } else {
                    printf("NO
    ") ;
                }
            }
        }
    
        return 0 ;
    }

     2.1bellman_ford 实现

    Source Code
    Problem: 3259        User: 201616040106
    Memory: 268K        Time: 141MS
    Language: C++        Result: Accepted
    
        Source Code
    
        #include <cstdio>
        #include <cstring>
        #include <iostream>
        #include <algorithm>
    
        using namespace std ;
    
        #define maxn 600
        #define inf 99999999
        int total ;
        int dis[maxn] ;
    
        struct node {
            int u ,
                v ;
            int w ;
    
        };
        node bian[10*maxn] ;
    
        void add(int u , int v , int c ){
    
            bian[total].u = u ;
            bian[total].v = v ;
            bian[total].w = c ;
            total ++ ;
        }
    
        bool bellman_ford(int n ){
    
            for(int i=1 ; i<=n ; i++){
                dis[i] = inf ;
            }
            dis[1] = 0 ;
    
     
            for(int j=1 ; j<=n-1 ; j++){
                for(int i=0 ;  i<total ; i++){
                    if(dis[bian[i].v] > dis[bian[i].u] + bian[i].w){
                        dis[bian[i].v] = dis[bian[i].u] + bian[i].w ;
                        
                    }
                }
    
            }
    
    
            for(int i=0 ; i<total ; i++){
                // 存在 负权回路 dis[bian[i].v] 是上面计算过的
                // dis[bian[i].u] + bian[i].w 是再次计算的  再次计算更小 存在负权回路
                if(dis[bian[i].v] > dis[bian[i].u] + bian[i].w){
                    return true ;
                }
            }
            return false ;
        }
    
        int main(){
    
            int F ;
            int n , m , w ;
            int s , e , t ;
    
            while(~scanf("%d" , &F)){
                while(F--){
                    scanf("%d%d%d" , &n , &m , &w) ;
                    total = 0 ;
                    for(int i=1 ; i<=m ; i++){
                        scanf("%d%d%d" , &s , &e , &t) ;
                        add(s , e , t ) ;
                        add(e , s , t ) ;
                    }
    
                    for(int i=1 ; i<=w ; i++){
                        scanf("%d%d%d" , &s , &e , &t ) ;
                        add(s , e , -t) ;
                    }
    
                    if(bellman_ford(n)){
                        printf("YES
    ") ;
                    } else {
                        printf("NO
    ") ;
                    }
                }
            }
    
            return 0 ;
        }

    2.2bellman_ford (小优化)

    Source Code
    Problem: 3259        User: 201616040106
    Memory: 268K        Time: 125MS
    Language: C++        Result: Accepted
    
        Source Code
    
        #include <cstdio>
        #include <cstring>
        #include <iostream>
        #include <algorithm>
    
        using namespace std ;
    
        #define maxn 600
        #define inf 99999999
        int total ;
        int dis[maxn] ;
    
        struct node {
            int u ,
                v ;
            int w ;
    
        };
        node bian[10*maxn] ;
    
        void add(int u , int v , int c ){
    
            bian[total].u = u ;
            bian[total].v = v ;
            bian[total].w = c ;
            total ++ ;
        }
    
        bool bellman_ford(int n ){
    
            for(int i=1 ; i<=n ; i++){
                dis[i] = inf ;
            }
            dis[1] = 0 ;
    
            bool flag = false ;
            for(int j=1 ; j<=n-1 ; j++){
                for(int i=0 ;  i<total ; i++){
                    if(dis[bian[i].v] > dis[bian[i].u] + bian[i].w){
                        dis[bian[i].v] = dis[bian[i].u] + bian[i].w ;
                        flag = true ;
                    }
                }
                if(!flag){
                    return false ;
                }
            }
    
    
            for(int i=0 ; i<total ; i++){
                // 存在 负权回路 dis[bian[i].v] 是上面计算过的
                // dis[bian[i].u] + bian[i].w 是再次计算的  再次计算更小 存在负权回路
                if(dis[bian[i].v] > dis[bian[i].u] + bian[i].w){
                    return true ;
                }
            }
            return false ;
        }
    
        int main(){
    
            int F ;
            int n , m , w ;
            int s , e , t ;
    
            while(~scanf("%d" , &F)){
                while(F--){
                    scanf("%d%d%d" , &n , &m , &w) ;
                    total = 0 ;
                    for(int i=1 ; i<=m ; i++){
                        scanf("%d%d%d" , &s , &e , &t) ;
                        add(s , e , t ) ;
                        add(e , s , t ) ;
                    }
    
                    for(int i=1 ; i<=w ; i++){
                        scanf("%d%d%d" , &s , &e , &t ) ;
                        add(s , e , -t) ;
                    }
    
                    if(bellman_ford(n)){
                        printf("YES
    ") ;
                    } else {
                        printf("NO
    ") ;
                    }
                }
            }
    
            return 0 ;
        }

    3. spfa(引用)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <stdlib.h>
    #include <math.h>
    #include <queue>
    #include <algorithm>
    using namespace std;
    #define N 5210
    #define INF 0xfffffff
    
    int cnt, dist[N], Head[N], num[N], vis[N];
    int n, m, w;
    
    struct Edge
    {
        int v, w, next;
    }e[N];
    
    void Add(int u, int v, int w)
    {
        e[cnt].v = v;
        e[cnt].w = w;
        e[cnt].next = Head[u];
        Head[u] = cnt++;
    }
    
    bool spfa()///spfa模板;
    {
        memset(vis, 0, sizeof(vis));
        memset(num, 0, sizeof(num));
        queue<int>Q;
        vis[1] = 1;
        dist[1] = 0;
        Q.push(1);
        num[1]++;
        while(Q.size())
        {
            int p=Q.front();
            Q.pop();
            vis[p] = 0;
            for(int i=Head[p]; i!=-1; i=e[i].next)
            {
                int q = e[i].v;
                if(dist[q] > dist[p] + e[i].w)
                {
                    dist[q] = dist[p] + e[i].w;
                    if(!vis[q])
                    {
                        vis[q] = 1;
                        Q.push(q);
                        num[q] ++;
                        if(num[q]>n)
                            return true;
                    }
                }
            }
        }
        return false;
    }
    
    int main()
    {
        int T, a, b, c;
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d%d%d", &n, &m, &w);
    
            cnt = 0;
            memset(Head, -1, sizeof(Head));
            for(int i=1; i<=n; i++)
                dist[i] = INF;
    
            for(int i=1; i<=m; i++)
            {
                scanf("%d%d%d", &a, &b, &c);
                Add(a, b, c);
                Add(b, a, c);
            }
            for(int i=1; i<=w; i++)
            {
                scanf("%d%d%d", &a, &b, &c);
                Add(a, b, -c);
            }
    
            if( spfa() )///存在负环;
                printf("YES
    ");
            else
                printf("NO
    ");
        }
        return 0;
    }
  • 相关阅读:
    几种常用的曲线
    0188. Best Time to Buy and Sell Stock IV (H)
    0074. Search a 2D Matrix (M)
    0189. Rotate Array (E)
    0148. Sort List (M)
    0859. Buddy Strings (E)
    0316. Remove Duplicate Letters (M)
    0452. Minimum Number of Arrows to Burst Balloons (M)
    0449. Serialize and Deserialize BST (M)
    0704. Binary Search (E)
  • 原文地址:https://www.cnblogs.com/yi-ye-zhi-qiu/p/7775448.html
Copyright © 2011-2022 走看看