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\n");
        else
            printf("NO\n");
    }
    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\n");
        else
            printf("NO\n");
    }
    return 0;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM