题目:
给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图G 的最小路径覆盖。
分析:
将每个点拆成两个点,比如将x拆成x1,x2,将y拆成y1,y2。假设 x < y,并且有一条 x 到 y 的边,那么在图中从 x1 连一条到 y2 的边。建完图后,得到一个二分图,求最大匹配,跑一边最大流即可。
- 最小路径覆盖数 = n - 最大流, n是拆点之前的点数
可以这样考虑,最开始没边的时候,每个点都独立需要一个路径覆盖,所以此时路径覆盖是 n 。每增加一条匹配,就会两个点共用一条路径,也就少了一条路径。所以 n - 最大匹配即答案。
最大匹配若等于 n ,说明原图有环,此时答案是多少应根据题意来判断。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 305;
const int MAXM = 12005;
const int INF = 0x3f3f3f3f;
struct Edge {
int to, next, cap, flow;
} edge[MAXM];
int tot, head[MAXN];
int Q[MAXN], cur[MAXN], dep[MAXN];
int n, m, S, T, N, to[MAXN],mark[MAXN];
void init() {
tot = 2;
memset(head, -1, sizeof head);
memset(to, -1, sizeof to);
memset(mark,0,sizeof mark);
}
void addedge(int u, int v, int w, int rw = 0) {
edge[tot].to = v; edge[tot].cap = w; edge[tot].flow = 0;
edge[tot].next = head[u]; head[u] = tot++;
edge[tot].to = u; edge[tot].cap = rw; edge[tot].flow = 0;
edge[tot].next = head[v]; head[v] = tot++;
}
bool bfs(int s, int t, int n) {
int Front = 0, tail = 0;
memset(dep, -1, sizeof(dep[0])*(n+1));
dep[s] = 0;
Q[tail++] = s;
while (Front < tail) {
int u = Q[Front++];
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (edge[i].cap > edge[i].flow && dep[v] == -1) {
dep[v] = dep[u] + 1;
if (v == t) return true;
Q[tail++] = v;
}
}
}
return false;
}
int dfs(int u, int f) {
if (u == T) return f;
int used = 0, rflow = 0;
for (int i = cur[u]; i != -1; i = edge[i].next) {
cur[u] = i;
int v = edge[i].to, w = edge[i].cap - edge[i].flow;
if (w > 0 && dep[v] == dep[u] + 1) {
if ((rflow = dfs(v, min(w, f - used)))) {
to[u] = v;
if(v>n) mark[v-n]=1;
used += rflow;
edge[i].flow += rflow;
edge[i ^ 1].flow -= rflow;
if (used == f) break;
}
}
}
if (!used) dep[u] = -1;
return used;
}
int dinic(int s, int t, int n) {
int maxflow = 0;
while (bfs(s, t, n)) {
for (int i = 0; i <= n; i++) cur[i] = head[i];
maxflow += dfs(s, INF);
}
return maxflow;
}
int main() {
ios::sync_with_stdio(false);
// freopen("1.txt", "r", stdin);
while (cin >> n >> m) {
init();
S = 2 * n + 1, T = 2 * n + 2, N = 2 * n + 2;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
if (a > b) swap(a, b);
addedge(a, b + n, 1);
}
for (int i = 1; i <= n; i++) {
addedge(S, i, 1);
addedge(i + n, T, 1);
}
int ans = n - dinic(S, T, N);
for(int i=1;i<=n;i++){
if(mark[i]) continue;
cout << i;
int k=i;
while(to[k]!=-1){
cout << " " << to[k]-n;
k = to[k] - n;
}
cout << "\n";
}
cout << ans << "\n";
}
return 0;
}