zoukankan      html  css  js  c++  java
  • Wormholes---poj3259(最短路 spfa 判断负环 模板)

    题目链接:http://poj.org/problem?id=3259

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

    虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退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,则说明存在负权回路。

     BellmanFord:

    #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];
    int n, m, w;
    
    struct Edge
    {
        int u, v, w, next;
    }e[N];
    
    void Add(int u, int v, int w)
    {
        e[cnt].u = u;
        e[cnt].v = v;
        e[cnt].w = w;
        e[cnt].next = Head[u];
        Head[u] = cnt++;
    }
    
    bool BellmanFord()
    {
        dist[1] = 0;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<cnt; j++)
            {
                if(dist[e[j].v] > dist[e[j].u]+e[j].w)
                    dist[e[j].v] = dist[e[j].u]+e[j].w;
            }
        }
        for(int i=0; i<cnt; i++)
        {
            if(dist[e[i].v] > dist[e[i].u]+e[i].w)
                return 0;
        }
        return 1;
    }
    
    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( !BellmanFord() )
                printf("YES
    ");
            else
                printf("NO
    ");
        }
        return 0;
    }
    View Code

     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;
    }
    View Code
  • 相关阅读:
    Count and Say leetcode
    Find Minimum in Rotated Sorted Array II leetcode
    Find Minimum in Rotated Sorted Array leetcode
    Search in Rotated Sorted Array II leetcode
    search in rotated sorted array leetcode
    Substring with Concatenation of All Words
    Subsets 子集系列问题 leetcode
    Sudoku Solver Backtracking
    Valid Sudoku leetcode
    《如何求解问题》-现代启发式方法
  • 原文地址:https://www.cnblogs.com/zhengguiping--9876/p/4797195.html
Copyright © 2011-2022 走看看