混合图欧拉回路

http://acm.pku.edu.cn/JudgeOnline/problem?id=1273

给一组边 有的是有向边有的是无向边 问是否存在欧拉回路

我们知道如果每个点入度等于出度 就存在欧拉回路

这里有无向边 可以任意指定方向, 有向边已经没法改了 只统计度数 不加边

那我们不妨指定方向为u->v 然后尝试更改这些边的方向来使每个点入度等于出度

使边u->v反向 那么u的出度-1 入度+1, v出度+1 入度-1

可以开个det数组 存每个点的出度减入度 通过改变边u->v的方向 使det[u]-=2, det[v]+=2

det为0时这个点出度等于入度 也就是说如果det为奇数一定没有欧拉回路

det[i]>0 加边st->i(权值为det[i]/2) det[i]<0 加边i->ed(权值为-det[i]/2)  (权值表示需要逆的边数)

跑个网络流 如果每个st出发的边都为满流 就有欧拉回路

因为考虑一个st...i...j...k...ed的增广路

路径上的边全部反向 det[i]-=2 det[k]+=2 det[j]不变

如果能给每个st出发的边都加上满流 那么就能所有点det为0

 

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 207, maxm = 1007, inf = 0x3f3f3f3f;
struct Edge{
    int u, v, w, f, nxt;
    Edge(){}
    Edge(int u, int v, int w, int f, int nxt):u(u), v(v), w(w), f(f), nxt(nxt){}
}edge[maxm*2];
int head[maxn], cur[maxn], gap[maxn], dep[maxn], pre[maxn], n, m, tot, st, ed;
int in[maxn], out[maxn], change[maxn];
void addedge(int u, int v, int w){
    edge[tot] = Edge(u, v, w, 0, head[u]);
    head[u] = tot++;
    edge[tot] = Edge(v, u, 0, 0, head[v]);
    head[v] = tot++;
}
void init(){
    tot = 0;
    memset(head, -1, sizeof head);
    memset(in, 0, sizeof in);
    memset(out, 0, sizeof out);
    memset(change, 0, sizeof change);
}
int sap(){
    memset(dep, 0, sizeof dep);
    memset(gap, 0, sizeof gap);
    memcpy(cur, head, sizeof head);
    int u = st;
    gap[0] = n;
    pre[u] = -1;
    int ans = 0;
    while(dep[st] < n){
        if(u == ed){
            int MIN = inf;
            for(int i = pre[ed]; ~i; i = pre[edge[i].u]){
                if(MIN > edge[i].w-edge[i].f)
                    MIN = edge[i].w-edge[i].f;
            }
            for(int i = pre[ed]; ~i; i = pre[edge[i].u]){
                edge[i].f += MIN;
                edge[i^1].f -= MIN;
            }
            ans += MIN;
            u = st;
            continue;
        }
        int v= 0;
        bool flag = false;
        for(int i = cur[u]; ~i; i = edge[i].nxt){
            v = edge[i].v;
            if(edge[i].w-edge[i].f && dep[u] == dep[v]+1){
                pre[v] = i;
                cur[u] = i;
                flag = true;
                break;
            }
        }
        if(flag){
            u = v;
            continue;
        }
        int MIN = n;
        for(int i = head[u]; ~i; i = edge[i].nxt){
            int v = edge[i].v;
            if(MIN > dep[v] && edge[i].w-edge[i].f){
                MIN = dep[v];
                cur[u] = i;
            }
        }
        gap[dep[u]]--;
        if(!gap[dep[u]])
            return ans;
        dep[u] = MIN+1;
        gap[dep[u]]++;
        if(u != st)
            u = edge[pre[u]].u;
    }
    return ans;
}
bool judge(){
    int sum = 0;
    for(int i = 1; i <= n; i++){
        out[i] = out[i]-in[i];
        if(!change[i] && out[i])
            return false;
        if(out[i]&1)
            return false;
        out[i] /= 2;
        if(out[i]<0){
            out[i] = -1*out[i];
            addedge(i, ed, out[i]);
        }
        else if(out[i] == 0)
            continue;
        else{
            sum += out[i];
            addedge(st, i, out[i]);
        }
    }
    n+=2; // mmmmmmp
    int ans = sap();
    //printf("%d %d\n", sum, ans);
    return sum == ans;
}
int main(){
    int t;
    scanf("%d", &t);
    while(t--){
        init();
        scanf("%d%d", &n, &m);
        st = 0, ed = n+1;
        while(m--){
            int u, v, jud;
            scanf("%d%d%d", &u, &v, &jud);
            out[u]++;
            in[v]++;
            if(!jud){
                addedge(u, v, 1);
                change[u] = change[v] = 1;
            }
        }
        if(judge())
            printf("possible\n");
        else
            puts("impossible");
    }
    return 0;
}

 

posted @ 2017-09-29 23:36  DearDongchen  阅读(182)  评论(0编辑  收藏  举报