• BZOJ2229: [Zjoi2011]最小割(最小割树)


    传送门

    最小割树

    算法

    初始时把所有点放在一个集合
    从中任选两个点出来跑原图中的最小割
    然后按照 (s) 集合与 (t) 集合的归属把当前集合划分成两个集合,递归处理
    这样一共跑了 (n − 1) 次最小割
    可以证明图中任意一对点之间的最小割的数值都包含在这 (n − 1) 个数值当中
    把每次求出的最小割看成是两个点之间的边,可以建出一棵树

    定理1

    任意三点之间的最小割一定是两个相等的较小值和一个较大值

    证明
    设任意三点 (a, b, c) 之间的最小割分别为 (mincut(a, b), mincut(a, c), mincut(b, c))
    (mincut(a, b)) 是这三者中的最小值
    那么在做 (a − b) 割时,(c) 一定属于其中的某一个集合,设其与 (a) 同属于一个集合
    那么就有 (mincut(b, c) le mincut(a, b))
    又因为 (mincut(a, b) le mincut(b, c)),所以两者相等

    定理2

    图中至多只有 (n − 1) 种不同的最小割

    证明
    编了一个构造的证明
    首先根据定理 (1) ,可以转化成如下问题:

    (n) 个点的无向完全图,要给每一条边染色,要求每个三元环上有两条边同色,求最多可以染多少种颜色

    考虑归纳法
    假设现在已经有一条长度大于 (1) 的链 (a)(b) 上的边的颜色互不相同
    考虑染 ((a,b)) 这条边
    如果链长 (=2),那么 ((a,b)) 只能选择链上某条边的颜色
    如果链长 (>2),假设两个端点在链上的不是 ((a,b)) 的边的颜色都是链上某条边的颜色
    那么取出其中两条 ((a,c))((c,b))((a,b)) 只能选择两个中其中一条边的颜色,即链上某条边的颜色

    所以可以得到,颜色互不相同的边不能形成环,所以最优的显然是树,即 (n-1) 中颜色

    Sol

    至此问题已经完美解决
    建出最小割树,任意两个点的最小割就是路径边权最小值
    直接暴力也可以
    复杂度貌似 (Theta(n^3m)) (???)

    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int maxn(505);
    const int inf(1e9);
    
    int first[maxn], n, m, cnt, lev[maxn], vis[maxn], s, t, id[maxn], tmp[maxn], cur[maxn], mincut[maxn][maxn], record[maxn << 4];
    queue <int> q;
    
    struct Edge {
        int to, next, w;
    } edge[maxn << 4];
    
    inline void Add(int u, int v, int w) {
        edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
        edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
        record[cnt - 1] = record[cnt - 2] = w;
    }
    
    inline int Bfs() {
        memset(lev, 0, sizeof(lev)), lev[s] = 1, q.push(s);
        register int u, e, v;
        while (!q.empty()) {
            for (u = q.front(), q.pop(), e = first[u]; ~e; e = edge[e].next)
                if (edge[e].w && !lev[v = edge[e].to]) lev[v] = lev[u] + 1, q.push(v);
        }
        return lev[t];
    }
    
    int Dfs(int u, int maxf) {
        if (u == t) return maxf;
        register int ret = 0, &e = cur[u], f, v;
        for (; ~e; e = edge[e].next)
            if (edge[e].w && lev[v = edge[e].to] == lev[u] + 1){
                f = Dfs(v, min(edge[e].w, maxf - ret));
                ret += f, edge[e].w -= f, edge[e ^ 1].w += f;
                if (ret == maxf) return ret;
            }
        if (!ret) lev[u] = 0;
        return ret;
    }
    
    inline int Dinic() {
        register int ret = 0, i;
        for (i = 0; i < cnt; ++i) edge[i].w = record[i];
        while (Bfs()) memcpy(cur, first, sizeof(cur)), ret += Dfs(s, inf);
        return ret;
    }
    
    void Mark(int u) {
        if (vis[u]) return;
        register int e;
        for (vis[u] = 1, e = first[u]; ~e; e = edge[e].next) if (edge[e].w) Mark(edge[e].to);
    }
    
    inline void Solve(int l, int r) {
        if (l >= r) return;
        memset(vis, 0, sizeof(vis)), s = id[l], t = id[r];
        register int i, j, mid, d = Dinic();
        for (Mark(s), j = 0, i = l; i <= r; ++i) if (vis[id[i]]) tmp[++j] = id[i];
        for (mid = l + j - 1, i = l; i <= r; ++i) if (!vis[id[i]]) tmp[++j] = id[i];
        for (i = l; i <= r; ++i) id[i] = tmp[i - l + 1];
        for (i = 1; i <= n; ++i)
            if (vis[i])
                for (j = 1; j <= n; ++j)
                    if (i != j && !vis[j]) mincut[i][j] = mincut[j][i] = min(mincut[i][j], d);
        Solve(l, mid), Solve(mid + 1, r);
    }
    
    int main() {
        register int i, j, u, v, w, test;
        scanf("%d", &test);
        while (test--) {
            memset(first, -1, sizeof(first)), cnt = 0;
            scanf("%d%d", &n, &m);
            for (i = 1; i <= m; ++i) scanf("%d%d%d", &u, &v, &w), Add(u, v, w);
            for (i = 1; i <= n; ++i) id[i] = i;
            memset(mincut, 63, sizeof(mincut)), Solve(1, n), scanf("%d", &w);
            while (w--) {
                scanf("%d", &u), v = 0;
                for (i = 1; i <= n; ++i)
                    for (j = i + 1; j <= n; ++j) v += mincut[i][j] <= u;
                printf("%d
    ", v);
            }
            putchar('
    ');
        }
        return 0;
    }
    

    参考文献

    1. 某鸽姓选手的网络流课件
      2. 垃圾博主自己yy
  • 相关阅读:
    .net Ioc 之 Unity 适合刚开始使用
    今天研究Unity Ioc 框架
    C#连接周立功CAN盒,调用ZCAN_OpenDevice函数时返回值一直是0失败。
    CAN通讯
    初学winform 自定义控件
    linq联表查询的时候报错
    dot net core 在IIS发布后,打开网页报错
    网卡驱动正常,网线也正常,但是网线插入电脑网口,网口指示灯就是不亮,网络适配器显示网线拔出
    flutter环境配置(windows)
    C#连接oracle数据库报错:OCIEnvCreate 失败,返回代码为 -1,但错误消息文本不可用
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/10091155.html
Copyright © 2020-2023  润新知