poj -- 1637 Sightseeing tour(混合圖歐拉回路)

給一個n個頂點,m條邊(既有有向邊又有無向邊)。求圖中是否存在歐拉回路。

http://poj.org/problem?id=1637

用最大流求解。

把該圖的無向邊隨便定向,計算每個點的入度和出度。如果有某個點出入度之差爲奇數,那麼肯定不存在歐拉回路。因爲歐拉回路要求每點入度 = 出度,也就是總度數爲偶數,存在奇數度點必不能有歐拉回路。
現在每個點入度和出度之差均爲偶數。將這個偶數除以2,得x。即是說,對於每一個點,只要將x條邊反向(入>出就是變入,出>入就是變出),就能保證出 = 入。如果每個點都是出 = 入,那麼很明顯,該圖就存在歐拉回路。
現在的問題就變成了:該改變哪些邊,可以讓每個點出 = 入?構造網絡流模型。有向邊不能改變方向,所以不用考慮(在求出度和入度時是要考慮的)。開始已定向的無向邊,定的是什麼向,就把網絡構建成什麼樣,邊長容量上限1。另新建s和t。對於入 > 出的點u,連接邊(u, t)、容量爲x,對於出 > 入的點v,連接邊(s, v),容量爲x(注意對不同的點x不同。)。之後,察看是否有滿流的分配。有就是能有歐拉回路,沒有就是沒有。查看流值分配,將所有流量非 0(上限是1,流值不是0就是1)的邊反向,就能得到每點入度 = 出度的歐拉圖。
由於是滿流,所以每個入 > 出的點,都有x條邊進來,將這些進來的邊反向,OK,入 = 出了。對於出 > 入的點亦然。那麼,沒和s、t連接的點怎麼辦?和s連接的條件是出 > 入,和t連接的條件是入 > 出,那麼這個既沒和s也沒和t連接的點,自然早在開始就已經滿足入 = 出了。那麼在網絡流過程中,這些點屬於“中間點”。我們知道中間點流量不允許有累積的,這樣,進去多少就出來多少,反向之後,自然仍保持平衡。
所以,就這樣,混合圖歐拉回路問題,解了。

//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int maxn = 500000 + 10;

///init是初始化要在加邊之前初始化,然後調用max_flow(頂點數,邊數,源點, 匯點)
const int maxv = 200 + 10;///頂點個數
const int maxe = 10000 + 10;///邊數
struct node{
    int v, cap, next;
}edge[maxe];
int head[maxv], cnt;
int n, m;///n是節點個數,m是邊數
int st, ed;///st是源點,ed是匯點
int gap[maxv], h[maxv], d[maxv];
void addedge(int u, int v, int w){///有向圖加邊
    edge[cnt].v = v; edge[cnt].cap = w; edge[cnt].next = head[u]; head[u] = cnt++;///正向邊
    edge[cnt].v = u; edge[cnt].cap = 0; edge[cnt].next = head[v]; head[v] = cnt++;///反向邊
}
int dfs(int x, int cost){
    if(x == ed) return cost; ///當前節點是匯點,直接返回cost

    int can = cost, d, minh = n - 1;
    for(int i=head[x]; ~i; i=edge[i].next){
        int v = edge[i].v, w = edge[i].cap;
        if(w > 0){///如果這條邊的容量大於0
            if(h[v] + 1 == h[x]){///如果這是允許弧
                if(can > w) d = w;///如果當前弧的容量小於之前可增廣的容量
                else d = can;

                d = dfs(v, d);///從v開始可增廣的容量爲d

                ///更新弧的容量和可增廣的容量
                edge[i].cap -= d;
                edge[i ^ 1].cap += d;
                can -= d;

                if(h[st] >= n) return cost - can;
                if(!can) break;///不能再繼續增廣
            }
            if(h[v] < minh) minh = h[v];///更新最小標號
        }
    }
    if(can == cost){///如果沒有增廣...GAP
        gap[h[x]]--;
        if(gap[h[x]] == 0) h[st] = n;///存在斷層,沒有增廣路了
        h[x] = minh + 1;///重新標記,保證下次再訪問的時候有流量
        gap[h[x]]++;
    }
    return cost - can;///在這個點之前可以增廣的 - 訪問這個點之後可以增廣的 = 在這個點增廣的容量
}
int max_flow(int N, int M, int source, int sink){///SAP+GAP優化
    n = N; m = M;
    st = source; ed = sink;
    gap[0] = n;///初始有n個節點標號爲0
    int ret = 0;
    while(h[st] < n){
        ret += dfs(st, inf);
    }
    return ret;
}
void init(){
    memset(head, -1, sizeof(head)); cnt = 0;
    memset(h, 0, sizeof(h));///h[i]表示i節點的標號
    memset(gap, 0, sizeof(gap));///gap[i]表示標號爲i的節點個數
}

int in[maxv], out[maxv];
int T, N, M;
int main(){
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &N, &M);
        init();
        memset(in, 0, sizeof(in));
        memset(out, 0, sizeof(out));
        int sum = 0, x, bian = 0;
        for(int i=0; i<M; i++){
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            if(w == 0)addedge(u, v, 1), bian++;
            in[v]++; out[u]++;
        }
        bool flag = true;
        for(int i=1; i<=N; i++){
            x = abs(in[i] - out[i]);
            if(x & 1) {flag = false; break;}
            if(in[i] > out[i]) addedge(i, N + 1, x / 2), bian++, sum += x / 2;
            else if(out[i] > in[i]) addedge(0, i, x / 2), bian++;
        }
        if(flag && sum == max_flow(N + 2, bian, 0, N + 1)) puts("possible");
        else puts("impossible");
    }
    return 0;
}


順便。。。。

若有向圖G存在歐拉回路,則G聯通,對於所有頂點入度 = 出度。

若無向圖G存在歐拉回路,則G聯通,所有頂點度爲偶數。

發佈了76 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章