有向无环图的路径覆盖问题就是在途中找一些不相交的的简单路径,使之覆盖图中的所有顶点,且任何一个顶点有且只有一条路径与之关联(如果沿着这些路径种的每条路径从七点走到终点,那么恰好可以经过图中的每个顶点一次且仅一次)
从上述定义可以看到
(1)每个单独的定点走可以是一条路径
(2)如果存在一路径(p1,p2,p3……,pk)那么在覆盖图中,顶点,p1,p2,……pk不再与其他顶点之间存在有向边。
有向无环图的最小路径覆盖问题就是找出最小的路径条数,使这些路径成为原图的一个路径覆盖。
题目链接:http://poj.org/problem?id=1422
题意:在敌国的一个小镇中,所有的街道都是单行道,每条街道的两端均链接着不同的路口。同时,从一个路口漫开始,沿着单行道向前行走永远也回不到开始的那个路口,就是说,这个小镇的单行街道系统不存在回路。
为了完成战略,目标需要向小镇的某些路口空投一些伞兵,这样伞兵可以沿着单行道访问小镇上所有的路口,为了使代价最小。要求每个伞兵访问过的路口之间没有交集。开始时伞兵开始选择任意的路口空降。你的任务是编写一段程序,计算出能满足上述任务所需的空投伞兵的最少数量。
题目的大意是在一个有向无环图中,从一些顶点出发,能遍历图上所有顶点,要求初始选择的顶点数最少且顶点不重复。
如果将从某个顶点开始遍历的过程看成路径的选取,那么问题就转化成在有向无环图中找出最少不相交的简单路径。这些路径覆盖图中的所有顶点。这样,符合关于最小路径覆盖问题的定义。
样例中第一个可以转化为
这样一个看似没有和二分图有关的问题就构建出了二分图的模型
在有向无环图中,有如下公式
最小路径覆盖数 = 节点数 - 二分图最大匹配数。
证明如下
若原图对应的二分图的最大匹配数目为0,那么节点集合V*和v**之间不存在匹配边。则最小路径覆盖数 = 节点数 显然成立。
若此时在对应二分图G = (V*,V**,E)中天界一条匹配边(vi*,vj**),那么在原图的覆盖路径中存在一条vi到vj的有向边。也就是说vi 和 vj 在一条路径上,于是路径覆盖数可以减少一个。
如此时继续增加对应二分图的匹配边,每增加一条,路径覆盖数就减少一条;直到匹配边不能继续增加时,,路径覆盖数也就不能继续减小。由此可知,对应二分图中每条匹配边可以和原图路径覆盖中一条路径上的一条匹配边对应。
对于原图路径覆盖中的每条有向边(vi,vj),也可以在对应的二分图中做一条匹配边(vi*,vj**),这样得到的就是一个匹配图。
题目代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
const int MAXN=125;
using namespace std;
int n,k;
vector<int> g[MAXN];
int from[MAXN],tot;
bool use[MAXN];
bool match(int x){
for(int i=0;i<g[x].size();++i){
if(!use[g[x][i]]){
use[g[x][i]]=true;
if(from[g[x][i]]==-1||match(from[g[x][i]])){
from[g[x][i]]=x;
return true;
}
}
}
return false;
}
int hungary(){
tot=0;
memset(from,0xff,sizeof(from));
for(int i=1;i<=n;++i){
memset(use,0,sizeof(use));
if(match(i))
tot++;
}
return tot;
}
int main()
{
int T,u,v;
cin>>T;
while(T--){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i) g[i].clear();
for(int i=0;i<k;++i){
scanf("%d%d",&u,&v);
g[u].push_back(v);
}
printf("%d\n",n-hungary());
}
return 0;
}