最小路径覆盖
题目描述
一个有向无回路的图G=(V,E)的一个路径覆盖是一个其结点不相交的路径集合P,图中的每一个结点仅包含于P中的一条路径。路径可从任意结点开始和结束,且长度也为任意值,包括0。请写出一个有效算法,找出一个包含尽可能少的路径的路径覆盖图中的所有点。
例如下图至少用两条路径覆盖,路径可以是:1-5-3,2-4。
输入
输入文件第一行为n(n≤4000),表示图G的顶点个数,从第二行开始每行有两个数u,v表示存在边(u,v)。
输出
输出文件的第一行为所求的最少的路径覆盖数k。第二行至第k+1行为每条路径上以0结尾的顶点序列。
样例输入
5
1 2
1 5
2 3
2 4
5 3
样例输出
2
1 5 3 0
2 4 0
这题自己的代码调了一晚上+早上一节课莫名其妙调不过去--弃了 抄了同学的代码
这个问题乍一看似乎与网络流没有什么关系,一开始我想到的似乎是最小割
后来经过dalao们指导,知道了是把DAG中点每一个点拆成一个出点一个入点,然后重构成二分图。
因为最小路径覆盖中的每一个路径上点(非起点终点)的出度与入度都是1
所以就能用二分图最大匹配来求解最小路径覆盖
答案是n - 二分图最大匹配数
这里的解法用的是Dinic,对于二分图出点集合中的每一个点,连一条从S出发的边,对于每个出点集合中的点连一条边到T,跑一遍最大流就好了
下面是同学的代码:
1 #include<iostream> 2 #include<cmath> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<queue> 7 #include<algorithm> 8 #define inf 0x7fffffff 9 using namespace std; 10 struct edge { 11 int to, cap, next; 12 }e[400005]; 13 int head[8005], iter[8005], lev[8005], way[8005]; 14 bool mk[8005]; 15 int st, en, c, cnt = 1, s, t, n, m; 16 inline void ins(int x, int y, int cp) 17 { 18 cnt++; e[cnt].to = y; e[cnt].next = head[x]; head[x] = cnt; e[cnt].cap = cp; 19 cnt++; e[cnt].to = x; e[cnt].next = head[y]; head[y] = cnt; e[cnt].cap = 0; 20 } 21 void bfs() 22 { 23 memset(lev, -1, sizeof(lev)); 24 queue<int>q; 25 lev[0] = 0; q.push(0); 26 while (!q.empty()) { 27 int v = q.front(); q.pop(); 28 for (int i = head[v]; i; i = e[i].next) { 29 edge &ed = e[i]; 30 if (ed.cap>0 && lev[ed.to]<0) { 31 lev[ed.to] = lev[v] + 1; q.push(ed.to); 32 } 33 } 34 } 35 } 36 int dfs(int u, int f) 37 { 38 int used = 0; 39 if (u == t) return f; 40 for (int i = head[u]; i; i = e[i].next) 41 { 42 edge &ed = e[i]; 43 if (ed.cap>0 && lev[u]<lev[ed.to]) { 44 int w = dfs(ed.to, min(f - used, ed.cap)); 45 if (w>0) { 46 if (ed.to - n>0) { way[u] = ed.to - n; mk[ed.to - n] = true; } 47 ed.cap -= w; e[i ^ 1].cap += w; used += w; 48 if (used == f) return f; 49 } 50 } 51 } 52 if (!used) lev[u] = -1; 53 return used; 54 } 55 int dinic() 56 { 57 int fl = 0; 58 while (1) 59 { 60 bfs(); if (lev[t]<0) return fl; 61 int d = dfs(0, inf); if (d>0) fl += d; 62 } 63 } 64 int main() 65 { 66 scanf("%d", &n); s = 0; t = 2 * n + 1; 67 while (scanf("%d%d", &st, &en) == 2) ins(st, en + n, 1); 68 for (int i = 1; i <= n; i++) ins(0, i, 1); for (int i = 1; i <= n; i++)ins(i + n, 2 * n + 1, 1); 69 printf("%d ", n - dinic()); 70 for (int i = 1; i <= n; i++) { 71 if (mk[i]) continue; 72 for (int j = i; j; j = way[j]) printf("%d ", j); 73 printf("0 "); 74 } 75 return 0; 76 }
下面是自己的(恬不知耻的放上来)不知道哪里出BUG了QAQQ
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 5 const int Maxv = 8005, INF = 0x6ffffff; 6 int Head[Maxv], Next[Maxv << 1], V[Maxv << 1], W[Maxv << 1], Way[Maxv], Dep[Maxv], cnt = 1, n, s, t; 7 bool Mk[Maxv]; 8 9 void Add(int u, int v, int w){ 10 cnt++; 11 Next[cnt] = Head[u]; 12 V[cnt] = v; 13 W[cnt] = w; 14 Head[u] = cnt; 15 } 16 17 void Add_Edge(int u, int v, int w){ 18 Add(u, v, w); 19 Add(v, u, 0); 20 } 21 22 void BFS(){ 23 std::queue<int> Q; 24 memset(Dep, -1, sizeof(Dep)); 25 Dep[s] = 0; 26 Q.push(s); 27 while (!Q.empty()) { 28 int u = Q.front(); 29 Q.pop(); 30 for (int i = Head[u]; i; i = Next[i]) { 31 if (Dep[V[i]] < 0 && W[V[i]] > 0) { //没有增广过且剩余容量大于0 32 Dep[V[i]] = Dep[u] + 1; 33 Q.push(V[i]); 34 } 35 } 36 } 37 } 38 39 int DFS(int u, int dist){ 40 int flow = 0; 41 if (u == t) { 42 return dist; 43 } 44 for (int i = Head[u]; i; i = Next[i]) { 45 if (Dep[V[i]] == Dep[u] + 1 && W[i] > 0) { 46 int w = DFS(V[i], std::min(dist - flow, W[i])); 47 if (w > 0) { 48 if (V[i] - n > 0) { 49 Way[u] = V[i] - n; 50 Mk[V[i] - n] = true; 51 } 52 W[i] -= w; 53 W[i ^ 1] += w; 54 flow += w; 55 if (flow == dist) { 56 return dist; 57 } 58 } 59 } 60 } 61 if (!flow) { 62 Dep[u] = -1; 63 } 64 return flow; 65 } 66 67 int Dinic() { 68 int Ans = 0; 69 while (1) { 70 BFS(); 71 if (Dep[t] < 0) { 72 return Ans; 73 } 74 int d = DFS(0, INF); 75 if (d > 0) { 76 Ans += d; 77 } 78 } 79 } 80 81 int main(){ 82 int st, ed; 83 scanf("%d", &n); 84 s = 0; 85 t = 2 * n + 1; 86 while (scanf("%d%d", &st, &ed) == 2) { 87 Add_Edge(st, ed, 1); 88 } 89 for (int i = 1; i <= n; i++) { 90 Add_Edge(s, i, 1); 91 } 92 for (int i = 1; i <= n; i++) { 93 Add_Edge(i + n, t, 1); 94 } //连入超级S和超级T, 把DAG重构成二分图 95 printf("%d ", n - Dinic()); 96 for (int i = 1; i <= n; i++) { 97 if (Mk[i]) { 98 continue; 99 } 100 for (int j = 1; j; j = Way[j]) { 101 printf("%d ", j); 102 } 103 printf("0 "); 104 } 105 return 0; 106 }