洛谷 P2764 最小路径覆盖问题【最大流+拆点+路径输出】
题目链接:https://www.luogu.org/problemnew/show/P2764
题目描述
«问题描述:
给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图G 的最小路径覆盖。提示:设V={1,2,.... ,n},构造网络G1=(V1,E1)如下:
每条边的容量均为1。求网络G1的( 0 x , 0 y )最大流。
«编程任务:
对于给定的给定有向无环图G,编程找出G的一个最小路径覆盖。
输入输出格式
输入格式:件第1 行有2个正整数n和m。n是给定有向无环图G 的顶点数,m是G 的边数。接下来的m行,每行有2 个正整数i和j,表示一条有向边(i,j)。
输出格式:从第1 行开始,每行输出一条路径。文件的最后一行是最少路径数。
输入输出样例
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
说明
1<=n<=150,1<=m<=6000
由@zhouyonglong提供SPJ
题解:
最小路径覆盖问题。答案就是N-最大二分匹配(证明略,感觉hihoCoder上讲的很详细,推荐看)。
将每个点拆点分成AB两部分,做最大二分匹配。然后源点到A部的点连边,边权为1;B部点到汇点连边,边权为1,跑最大流......
关于路径输出问题,可以从汇点开始找残余容量为0的点作为起始点递归输出路径...
代码:
1 #include <cstdio> 2 #include <vector> 3 #include <algorithm> 4 #include <queue> 5 #include <cstring> 6 using namespace std; 7 const int N = 250*2+5; 8 const int M = 6000*2+5; 9 const int inf = 1e9; 10 int n, m, S, T; 11 int dep[N], cur[N]; 12 int head[N]; 13 struct Edge{ 14 int v, c, nex; 15 Edge(int _v=0,int _c=0,int _nex=0):v(_v),c(_c),nex(_nex){} 16 }E[M]; 17 18 int cnt; 19 void add(int u, int v, int c){ 20 E[cnt].v = v; 21 E[cnt].c = c; 22 E[cnt].nex = head[u]; 23 head[u] = cnt++; 24 } 25 26 bool bfs() { 27 queue<int> q; 28 memset(dep, -1, sizeof(dep)); 29 q.push(S); dep[S] = 0; 30 while(!q.empty()) { 31 int u = q.front(); q.pop(); 32 for(int i = head[u]; ~i; i = E[i].nex) { 33 int v = E[i].v; 34 if(E[i].c && dep[v] == -1) { 35 dep[v] = dep[u] + 1; 36 q.push(v); 37 } 38 } 39 } 40 return dep[T] != -1; 41 } 42 int dfs(int u, int flow) { 43 if(u == T) return flow; 44 int w, used=0; 45 for(int i = head[u]; ~i; i = E[i].nex) { 46 int v = E[i].v; 47 if(dep[v] == dep[u] + 1) { 48 w = flow - used; 49 w = dfs(v, min(w, E[i].c)); 50 E[i].c -= w; E[i^1].c += w; 51 if(v) cur[u] = i; 52 used += w; 53 if(used == flow) return flow; 54 } 55 } 56 if(!used) dep[u] = -1; 57 return used; 58 } 59 int dinic() { 60 int ans = 0; 61 while(bfs()) { 62 for(int i = 0; i <= T;i++) 63 cur[i] = head[i]; 64 ans += dfs(S, inf); 65 } 66 return ans; 67 } 68 void print(int x, int &f) { 69 if(x <= S) return; 70 if(f == 1) f = 0; 71 else printf(" "); 72 printf("%d", x); 73 74 for(int i = head[x]; ~i; i = E[i].nex) { 75 if(!E[i].c){ 76 print(E[i].v - n, f); 77 } 78 } 79 } 80 int main() { 81 int i, j, u, v; 82 scanf("%d%d", &n, &m); 83 memset(head, -1, sizeof(head)); 84 cnt = 0; 85 S = 0; T = 2*n+1; 86 for(i = 0; i < m; ++i) { 87 scanf("%d%d", &u, &v); 88 add(u, v+n, 1); add(v+n, u, 0); 89 } 90 for(i = 1; i <= n; ++i) add(S,i,1),add(i,S,0); 91 for(i = 1; i <= n; ++i) add(i+n,T,1),add(T,i+n,0); 92 93 int ans = dinic(); 94 95 for(i = head[T]; ~i; i = E[i].nex) { 96 if(!E[i].c) { 97 int f = 1; 98 print(E[i].v - n, f); 99 puts(""); 100 } 101 } 102 printf("%d\n", n-ans); 103 return 0; 104 } 105 106 /* 107 7 7 108 1 2 109 1 3 110 2 4 111 3 4 112 4 5 113 4 6 114 5 7 115 */