「网络流 24 题」最小路径覆盖

链接

题解

  • 首先对有向无环图进行拆点,即将每个点拆为入点$i$和出点$i+n$,得到一张二分图
  • 结论:有向无环图的最小路径覆盖等于$n$-拆点二分图最大匹配数
  • 简单证明:因为此题要求路径不相交,所以每一个点的入度和出度都不超过1,而每个节点都会被覆盖即每个节点的入度和出度至少有一个为1.所以最小路径覆盖的边$(x,y)$对应拆点二分图中的一个匹配。显然最小路径覆盖数等于最终出度为0的点的个数,而出度为0的点对应拆点二分图左部的未匹配点个数。所以结论成立
  • 建完图跑dinic或者匈牙利都行
查看代码
#include 
using namespace std;
typedef long long ll;
const int maxn = 1e4 + 5;
const int mod = 1e9 + 7;
ll qpow(ll a, ll b)
{
    ll res = 1;
    for (; b; b >>= 1)
    {
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod;
    }
    return res;
}
struct graph
{
    int head[maxn], nxt[maxn << 1], to[maxn << 1], w[maxn << 1], sz;
    void init() { memset(head, -1, sizeof(head)); }
    graph() { init(); }
    void push(int a, int b, int c) { nxt[sz] = head[a], to[sz] = b, w[sz] = c, head[a] = sz++; }
    int &operator[](const int a) { return to[a]; }
} g;
int d[405], now[405];
int s, t;
bool bfs()
{
    memset(d, 0, sizeof(d));
    queue q;
    q.push(s);
    d[s] = 1;
    now[s] = g.head[s];
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        for (int i = g.head[x]; ~i; i = g.nxt[i])
        {
            if (!d[g[i]] && g.w[i])
            {
                d[g[i]] = d[x] + 1;
                now[g[i]] = g.head[g[i]];
                q.push(g[i]);
                if (g[i] == t)
                    return 1;
            }
        }
    }
    return 0;
}
int dinic(int x, int flow)
{
    if (x == t)
        return flow;
    int res = flow, i, k;
    for (i = now[x]; ~i && res; i = g.nxt[i])
    {
        if (d[g[i]] == d[x] + 1 && g.w[i])
        {
            k = dinic(g[i], min(res, g.w[i]));
            if (!k)
                d[g[i]] = 0;
            res -= k;
            g.w[i] -= k;
            g.w[i ^ 1] += k;
        }
    }
    now[x] = i;
    return flow - res;
}
int pre[maxn], son[maxn];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("simple.in", "r", stdin);
    freopen("simple.out", "w", stdout);
#endif
    int n, m;
    scanf("%d%d", &n, &m);
    s = 0, t = 2 * n + 1;
    for (int i = 1; i <= n; ++i)
    {
        pre[i] = son[i] = i;
        g.push(s, i, 1), g.push(i, s, 0);
        g.push(i + n, t, 1), g.push(t, i + n, 0);
    }
    for (int i = 1, a, b; i <= m; ++i)
    {
        scanf("%d%d", &a, &b);
        g.push(a, b + n, 1), g.push(b + n, a, 0);
    }
    int flow = 0, maxflow = 0;
    while (bfs())
    {
        while (flow = dinic(s, 1e9))
            maxflow += flow;
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = g.head[i]; ~j; j = g.nxt[j])
        {
            if (g[j] == 0 || g.w[j] == 1)
                continue;
            pre[g[j] - n] = i, son[i] = g[j] - n;
            break;
        }
    }
    vector path;
    for (int i = 1; i <= n; ++i)
    {
        if (son[i] == i)
        {
            int tmp = son[i];
            path.push_back(tmp);
            do
            {
                tmp = pre[tmp];
                path.push_back(tmp);
            } while (tmp != pre[tmp]);
            reverse(path.begin(), path.end());
            for (auto j : path)
                printf("%d ", j);
            printf("\n");
            path.clear();
        }
    }
    printf("%d\n", n - maxflow);
    return 0;
}

你可能感兴趣的:(「网络流 24 题」最小路径覆盖)