[网络流二十四题]最小路径覆盖问题

最小路径覆盖

题目描述

一个有向无回路的图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 }
 
原文地址:https://www.cnblogs.com/GldHkkowo/p/8872273.html