最小路径覆盖问题(网络流,二分图) & 最小路径点覆盖结论证明
Posted hsez-cyx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小路径覆盖问题(网络流,二分图) & 最小路径点覆盖结论证明相关的知识,希望对你有一定的参考价值。
题目描述
给定有向图 G=(V,E) 。设 P 是 G 的一个简单路(顶点不相交)的集合。
如果 V 中每个定点恰好在PP的一条路上,则称 P 是 G 的一个路径覆盖。
P 中路径可以从 V 的任何一个定点开始,长度也是任意的,特别地,可以为 0 。
G 的最小路径覆盖是 G 所含路径条数最少的路径覆盖。设计一个有效算法求一个 GAP (有向无环图) G 的最小路径覆盖。
输入格式
第一行有 2 个正整数 n 和 m 。 n 是给定 GAP(有向无环图) G 的顶点数, m 是 G 的边数。
接下来的 m 行,每行有两个正整数 i 和 j 表示一条有向边 (i,j)。
输出格式
从第 1 行开始,每行输出一条路径。文件的最后一行是最少路径数。
Solution
借此题的多种解法加深对最小路径点覆盖结论的理解
二分图做法
- 方法一
设最小路径点覆盖的边集为 E
当不考虑图中所有边时 E 中边数为0,最少路径数为 n
每合法地向 E 中加一条有向边,相当于将两条路径合二为一,最少路径数 - 1
那怎样的边合法呢?
当每个点至多为 E 中一条有向边的起点时, E 合法。
这与二分图中的 “ 1 要素”(每个点至多在一条边中)契合
于是我们将编号为 i 的点分成 i (左部)和 i+n (右部)
对于每条原图中的边,连接(i,j+n)
求答案为 n - 二分图最大匹配
- 方法二
还是建立方法一中的二分图,求最大匹配,匹配边为放入 E 中的边
此时每个原图中的点最多为一条被选择的原图中边的起点,最多为一条被选择的原图中边的终点
二分图右部的匹配点为一条被选择的原图中边的起点,左部的匹配点为一条被选择的原图中边的终点
考虑右部,每个右部的非匹配点为一条路径的终点,路径条数 = 终点数 = 右部非匹配点数
考虑左部,同理可得到答案
#include <cstdio> #include <cstdlib> #include <vector> using namespace std; const int N=310,M=6000; int head[N],tot,ver[M],nxt[M],edge[M]; int n,m,a,b,vis[N],cnt,ans,match[N]; bool flag[N]; vector <int> an; void add(int x,int y) { ver[++tot]=y,nxt[tot]=head[x],head[x]=tot; } bool dfs(int x) { for(int i=head[x],y;i;i=nxt[i]) if(vis[y=ver[i]]!=cnt) { vis[y]=cnt; if(!match[y] || dfs(match[y])) { match[y]=x,match[x]=y; return 1; } } return 0; } int main() { scanf("%d%d",&n,&m); while(m--) { scanf("%d%d",&a,&b); add(a,b+n); } for(int i=1;i<=n;i++) { cnt++; if(dfs(i)) ans++; } for(int i=1;i<=n;i++) { if(flag[i]) continue; int x=i; an.clear(); while(x>0) flag[x]=true,an.push_back(x),x=match[x]-n; int size=an.size(); for(int i=size-1;i>=0;i--) printf("%d ",an[i]); puts(""); } printf("%d ",n-ans); return 0; }
网络流做法
思路与二分图相似(似乎只是用网络流求二分图最大匹配???)
将编号为 i 的点拆成 i 和 i+n,代表作为一条边起点和终点的情况
i 与源点连一条容量为 1 的边,i+n 与汇点连一条容量为 1 的边,
代表它只能作为一条选中边的起点/终点一次
对于原图中的边(i,j),连建的图中的(i,j+n)
通过求最大流求出最多的合法的边
以上是关于最小路径覆盖问题(网络流,二分图) & 最小路径点覆盖结论证明的主要内容,如果未能解决你的问题,请参考以下文章
最小路径覆盖问题(网络流,二分图) & 最小路径点覆盖结论证明