最小路径覆盖

给定有向图 G = ( V , E ) G=(V,E) G=(V,E)。设 P P P G G G 的一个简单路(顶点不相交)的集合。如果 V V V 中每个顶点恰好在 P P P 的一条路上,则称 P P P G G G 的一个路径覆盖。P 中路径可以从 V V V 的任何一个顶点开始,长度也是任意的,特别地,可以为 0 0 0 G G G 的最小路径覆盖是 G G G 的所含路径条数最少的路径覆盖。

设计一个有效算法求一个有向无环图 G G G 的最小路径覆盖。

Input

1 1 1 行有 2 个正整数 n n n m m m n n n 是给定有向无环图 G G G 的顶点数, m m m G G G 的边数。
接下来的 m m m 行,每行有 2 2 2 个正整数 u u u v v v,表示一条有向边 ( i , j ) (i,j) (i,j)

Output

从第 1 1 1 行开始,每行输出一条路径。
文件的最后一行是最少路径数。

Example

样例输入

11 12
1 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11

样例输出

1 4 7 10 11
2 5 8
3 6 9
3

Hint

1 ≤ n ≤ 200 , 1 ≤ m ≤ 6000 1≤n≤200,1≤m≤6000 1n200,1m6000

思路

把原图的每个点 V V V 拆成 V x V_x Vx V y V_y Vy两个点,如果有一条有向边 A A A-> B B B,那么就加边 A x A_x Ax−> B y B_y By。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数 − - 新图的最大匹配数

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+7;
const int maxm=2e5+7;
const int inf=0x3f3f3f3f;

int n,m,u,v;
struct Dinic {
    struct Edge {
        int next, f, to;
    } e[maxm];
    int head[maxn], dep[maxn], tol, ans;
    int cur[maxn];
    int src, sink, n;

    void add(int u, int v, int f) {
        tol++;
        e[tol].to = v;
        e[tol].next = head[u];
        e[tol].f = f;
        head[u] = tol;
        tol++;
        e[tol].to = u;
        e[tol].next = head[v];
        e[tol].f = 0;
        head[v] = tol;
    }

    bool bfs() {
        queue<int> q;
        memset(dep, -1, sizeof(dep));
        q.push(src);
        dep[src] = 0;
        while (!q.empty()) {
            int now = q.front();
            q.pop();
            for (int i = head[now]; i; i = e[i].next) {
                if (dep[e[i].to] == -1 && e[i].f) {
                    dep[e[i].to] = dep[now] + 1;
                    if (e[i].to == sink)
                        return true;
                    q.push(e[i].to);
                }
            }
        }
        return false;
    }

    int dfs(int x, int maxx) {
        if (x == sink)
            return maxx;
        for (int &i = cur[x]; i; i = e[i].next) {
            if (dep[e[i].to] == dep[x] + 1 && e[i].f > 0) {
                int flow = dfs(e[i].to, min(maxx, e[i].f));
                if (flow) {
                    e[i].f -= flow;
                    e[i ^ 1].f += flow;
                    return flow;
                }
            }
        }
        return 0;
    }

    int dinic(int s, int t) {
        ans = 0;
        this->src = s;
        this->sink = t;
        while (bfs()) {
            for (int i = 0; i <= n; i++)
                cur[i] = head[i];
            while (int d = dfs(src, inf))
                ans += d;
        }
        return ans;
    }

    void init(int n) {
        this->n = n;
        memset(head, 0, sizeof(head));
        tol = 1;
    }
} G;
int pre[maxn],sec[maxn];

template<typename T>
inline void read(T &x) {
    x = 0;
    static int p;
    p = 1;
    static char c;
    c = getchar();
    while (!isdigit(c)) {
        if (c == '-')p = -1;
        c = getchar();
    }
    while (isdigit(c)) {
        x = (x << 1) + (x << 3) + (c - 48);
        c = getchar();
    }
    x *= p;
}
template <typename T>
inline void print(T x) {
    if (x<0) {
        x = -x;
        putchar('-');
    }
    static int cnt;
    static int a[50];
    cnt = 0;
    do {
        a[++cnt] = x % 10;
        x /= 10;
    } while (x);
    for (int i = cnt; i >= 1; i--)putchar(a[i] + '0');
    //puts("");
}
int isnot_head[maxn],to[maxn];
int main() {
    read(n);
    read(m);
    int S = 0, T = n * 2 + 1;
    G.init(T);
    for (int i = 1; i <= m; i++) {
        read(u);
        read(v);
        G.add(u, v + n, 1);
    }
    for (int i = 1; i <= n; i++) {
        G.add(S, i, 1);
    }
    for (int i = n + 1; i <= n * 2; i++) {
        G.add(i, T, 1);
    }
    for (int i = 0; i <= T; i++) to[i] = i;
    int ans = G.dinic(S, T);
    for (int i = 2; i <= G.tol; i+=2) {
        int u = G.e[i ^ 1].to;
        int v = G.e[i].to;
        if (u != S && v != T && G.e[i ^ 1].f != 0) {
            v -= n;
            to[u] = v;
            isnot_head[v] = 1;
        }
    }
    for (int i = 1; i <= n; i++) {
        int j = i;
        if (!isnot_head[j]) {
            printf("%d ", j);
            while (to[j] != j) {
                printf("%d ", to[j]);
                j = to[j];
            }
            puts("");
        }
    }
    print(n - ans);
    puts("");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值