zoukankan      html  css  js  c++  java
  • poj 1637 Sightseeing tour 混合图欧拉回路 最大流 建图

    题目链接

    题意

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

    思路

    从欧拉回路说起

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

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

    对无向边随意定向,得到一个有向图。在这个有向图中,如果有(forall vin 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;
    }
    
    
    
  • 相关阅读:
    Java中替换字符串中特定字符,replaceAll,replace,replaceFirst的区别
    牛客剑指offer 67题(持续更新~)
    从尾到头打印链表
    字符串变形
    缩写
    删除公共字符
    替换空格
    二维数组的查找
    acm博弈论基础总结
    acm模板总结
  • 原文地址:https://www.cnblogs.com/kkkkahlua/p/7773907.html
Copyright © 2011-2022 走看看