poj 1637 Sightseeing tour 混合图欧拉回路 最大流 建图

题目链接

题意

给定一个混合图,里面既有有向边也有无向边。问该图中是否存在一条路径,经过每条边恰好一次。

思路

从欧拉回路说起

首先回顾有向图欧拉回路的充要条件\(\forall v\in G, d_{in}(v)=d_{out}(v)\).

现在这个图中有一些无向边,那怎么办?
那就转化成有向边呀。

对无向边随意定向,得到一个有向图。在这个有向图中,如果有\(\forall v\in G, abs(d_{in}(v)-d_{out}(v))\)为偶数,则将其中一些边反向,肯定能得到一个欧拉图。而若为奇数,则肯定不可以。

为什么?可以有两种考虑方式:

1.将所有的边看成无向边,那么,“对于每个点来说入度与出度之差绝对值为偶数”,这个条件就意味着,“对于每个点其度(=入度+出度)为偶数”,而这正是无向图欧拉回路的充要条件。现在我们得到了这个无向图中的欧拉回路,一路走一路定向,就得到了有向图中的欧拉回路。对比原有向图中的边的方向与现在得到的欧拉回路中的边的方向,将其中一些反向即可。
2. 直接从修改的角度想,将一条边反向的效果是,它的两个端点的入度与出度之差都变化2,最终可以使得达到每个点的入度与出度之差为0的效果,即得到欧拉回路。
(第二种说得不太严谨...)

问题转化

但是这道题是不是这么简单的呢?并不是。
为什么?因为我们并不能将其中任意一条边随意反向,不然这就跟给了一张无向图没什么差别了。

所以,限制就在于:将限定范围内的一些边反向,问能否得到一个欧拉图。

于是问题转化为,现有一些点,其中一些入度\(\gt\)出度,另一些出度\(\gt\)入度。将其中一些边反向,问能否满足所有的点的入度\(==\)出度

建图

由上面的关系可以很容易联想到网络流。

因为有向边是不可以反向的,所以对度数的改变没有任何贡献,不加入图中。

而经过定向成为有向边的边\(e=(u,v)\)在这一模型中的贡献是通过其反向可使\(u\)的入度-出度值增加\(2\)\(v\)的出度-入度值增加\(2\),故将之加入图中,权值为\(2\),意为将这一条边反向对度数的改变贡献为\(2\)

接下来的步骤就很顺理成章了:

  1. 在 源点 到 出度\(\gt\)入度的点 之间加边,边权为出度与入度之差
  2. 在 入度\(\gt\)出度的点 到 汇点 之间加边,边权为入度与出度之差

最后只需判断最大流是否为满流即可。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
#define maxn 1010
#define maxm 10010
#define inf 0x3f3f3f3f
using namespace std;
typedef long long LL;
struct Edge { int to, ne, c; }edge[maxm];
int dep[maxn], ne[maxn], n, m, tot, s,t, out[maxn], in[maxn];
void add(int u, int v, int c) {
    edge[tot] = {v, ne[u], c};
    ne[u] = tot++;
    edge[tot] = {u, ne[v], 0};
    ne[v] = tot++;
}
int bfs(int src) {
    memset(dep, 0, sizeof dep);
    dep[src] = 1;
    queue<int> q;
    while (!q.empty()) q.pop();
    q.push(src);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int i = ne[u]; ~i; i = edge[i].ne) {
            int v = edge[i].to;
            if (edge[i].c > 0 && !dep[v]) dep[v] = dep[u] + 1, q.push(v);
        }
    }
    return dep[t];
}
int dfs(int u, int flow) {
    if (u == t) return flow;
    int ret = 0;
    for (int i = ne[u]; ~i; i = edge[i].ne) {
        int v = edge[i].to;
        if (edge[i].c > 0 && dep[v] == dep[u] + 1) {
            int c = dfs(v, min(flow-ret, edge[i].c));
            edge[i].c -= c;
            edge[i^1].c += c;
            ret += c;
            if (ret == flow) break;
        }
    }
    if (!flow) dep[u] = 0;
    return ret;
}
void work() {
    scanf("%d%d", &n, &m);
    tot = 0; memset(ne, -1, sizeof ne);
    memset(out, 0, sizeof out); memset(in, 0, sizeof in);
    s = 0, t = n+1;
    for (int i = 0; i < m; ++i) {
        int u, v, t;
        scanf("%d%d%d", &u, &v, &t);
        if (!t) add(u, v, 2);
        ++out[u], ++in[v];
    }
    int cnt=0;
    for (int i = 1; i <= n; ++i) {
        if (abs(out[i]-in[i])&1) { puts("impossible"); return; }
        if (out[i] > in[i]) add(s, i, out[i]-in[i]), cnt += out[i]-in[i];
        else if (out[i] < in[i]) add(i, t, in[i]-out[i]);
    }
    int ret=0,ans=0;
    while (bfs(s)) {
        while (ret = dfs(s, inf)) ans += ret;
    }
    if (ans == cnt) puts("possible");
    else puts("impossible");
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}


posted @ 2017-11-02 19:24  救命怀  阅读(96)  评论(0编辑  收藏  举报