最小路径覆盖问题(网络流24题)

洛谷连接:最小路径覆盖问题c++

题目描述

«问题描述:算法

给定有向图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 )最大流。spa

«编程任务:设计

对于给定的给定有向无环图G,编程找出G的一个最小路径覆盖。rest

输入输出格式

输入格式:code

 

件第1 行有2个正整数n和m。n是给定有向无环图G 的顶点数,m是G 的边数。接下来的m行,每行有2 个正整数i和j,表示一条有向边(i,j)。blog

 

输出格式:ci

 

从第1 行开始,每行输出一条路径。文件的最后一行是最少路径数。

 

输入输出样例

输入样例#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: 
1 4 7 10 11
2 5 8
3 6 9
3

说明

1<=n<=150,1<=m<=6000

 

可能不少人看到这个提示反而更懵逼了。先解释一下题意,大概就是给你有向图,样例是一张这样的图:

 

而后求出的最小路径数就是3了,三条路径也很容易能够看出。

咱们先考虑一下如何求出二分图的最小路径覆盖。

这里介绍一个定理:

最小路径覆盖数=|G|-二分图最大匹配数(|G|是有向图中的总边数)

为何是这样的呢?首先咱们知道,在二分图中一个点就表明者一条路径。那么若是此时二分图内没有连边,这个公式是成立的。每当二分图内增长一条边,最大匹配数就会+1,而一条匹配边会链接二分图中的两个点,那么两个点间原本有两条路径覆盖,就变成了一条。同理,每加入一条边匹配数就会+1,路径覆盖数就会-1。因此这个公式是成立的。可是这个公式是对二分图适用的,如何将它转化到这个问题上来呢?

 

这时咱们先将一个点拆A成出点Ax和入点Ay,那么在连有向边A -> B的时候就将Ax连向By。就将有向图变成了一个二分图。咱们看一下样例转化为二分图的样子(n == 11)。

 那么这样咱们只须要求出将每一个点拆点组成二分图后的最大匹配就能够了。

最后在输出路径的时候就记录下每一个入点的连入边,而后用并查集记录一下一条路径的起点就行了。

 

 1 #include<bits/stdc++.h>
 2 using namespace std;  3 const int N=15000+5;  4 const int inf=2147483647;  5 
 6 int n, m, s, t;  7 int cnt = 1;  8 int ans = 0;  9 int last[N*10]; 10 int lev[N*10]; 11 int fa[N*10]; 12 
13 struct edge{ 14     int to, next, cap, from; 15 }e[N*10]; 16 
17 void add(int x,int y,int z){ 18     e[++cnt].to = y; 19     e[cnt].from = x; 20     e[cnt].cap = z; 21     e[cnt].next = last[x]; 22     last[x] = cnt; 23 } 24 
25 bool bfs(){ 26     memset(lev,-1,sizeof(lev)); 27     queue <int> q; lev[s] = 0; q.push(s); 28     while(!q.empty()){ 29         int x = q.front(); q.pop(); 30         for(int i=last[x];i;i=e[i].next){ 31             int to = e[i].to; 32             if(e[i].cap && lev[to] == -1) 33                 lev[to] = lev[x]+1 , q.push(to); 34  } 35  } 36     return lev[t] != -1; 37 } 38 
39 int dfs(int x,int flow){ 40     if(x == t) return flow; 41     int rest = 0; 42     for(int i=last[x];i;i=e[i].next){ 43         int to = e[i].to; 44         if(lev[to] == lev[x]+1 && e[i].cap){ 45             int f = dfs(to , min(flow-rest,e[i].cap)); 46             if(f){ 47                 rest += f; 48                 e[i].cap -= f; 49                 e[i^1].cap += f; 50  } 51  } 52  } 53     return rest; 54 } 55 
56 int find(int x){ 57     if(fa[x] == x) return x; 58     return fa[x] = find(fa[x]); 59 } 60 
61 void output(int x){ 62     cout << x << ' '; 63     for(int i=last[x];i;i=e[i].next) 64         if(e[i].cap == 0 && e[i].to > n) 65             output(e[i].to-n); 66 } 67 
68 int main(){ 69     int x, y; cin >> n >> m; 70     s = 0 , t = n*2+1; 71     for(int i=1;i<=m;i++){ 72         cin >> x >> y; 73         add(x,y+n,1); add(y+n,x,0); 74  } 75     for(int i=1;i<=n;i++) add(s,i,1),add(i,s,0); 76     for(int i=n+1;i<=n*2;i++) add(i,t,1),add(t,i,0); 77     while(bfs()) ans += dfs(s,inf); 78     for(int i=1;i<=n;i++) fa[i] = i; 79     for(int i=2;i<=cnt;i++){ 80         if(e[i].cap == 0 && e[i].to>n && e[i].to<=n*2 && e[i].from>0 && e[i].from<=n) 81             if(fa[e[i].from] != fa[e[i].to-n]) 82                 fa[e[i].to-n] = fa[e[i].from]; 83  } 84     for(int i=1;i<=n;i++) 85         if(fa[i] == i) output(i), cout << endl; 86     cout << n-ans << endl; 87     return 0; 88 }

 

 

 

相关文章
相关标签/搜索