给定有向图 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 3Hint
1 ≤ n ≤ 200 , 1 ≤ m ≤ 6000 1≤n≤200,1≤m≤6000 1≤n≤200,1≤m≤6000
把原图的每个点 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; }