最小路径算法解决以下问题:对于一个DAG,找到最少的路径(连续的若干边)条数(单个顶点也算一条路径),覆盖所有顶点。按路径可否相交分为两类。
首先是路径不相交的最小覆盖:
我们可以把每个顶点拆为出点和入点,这时每种路径安排都是一个二分图P',问题转化为了求该二分图的最大匹配:如果在P'中增加一条匹配边pi'-->pj',那么在图P的路径覆盖中就存在一条由pi连接pj的边,也就是说pi与pj 在一条路径上,于是路径覆盖数就可以减少一个。
求解二分图最大匹配用匈牙利算法。
依次遍历每个点尝试寻找增广路。一条增广路满足两端是非匹配点,这样的路径可以通过调整使得匹配数加一。
值得注意的一点是,虽然我们将每个点拆分成了入点和出点,但match数组仍然只保存DAG中的from点,所以在遍历新的点时不需要加上 if(match[i]==0) 的判断,这里是和一般的二分图最大匹配模板有区别的地方。
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int n,m;
const int N=500;
int M=5000;
bool dis[N+1][N+1];
bool vis[N+1];
int match[N+1];
int dfs(int u){
//返回1:能找到增广路
for(int i=1;i<=n;i++){
if(dis[u][i]&&!vis[i]){
vis[i]=true;
if(match[i]==0||dfs(match[i])){ //如果这个点已经匹配了,就看它匹配的那个点能不能找到新的匹配
match[i]=u;
return 1;
}
}
}
return 0;
}
int hungary(){
int ans=0;
memset(match,0,sizeof(match));
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
ans+=dfs(i);
}
return n-ans; //返回最小路径数
}
如果是路径可相交的情况,通过Floyd求出原图的传递闭包。在这个图上求出的路径不可交的结果即为所求。
证明:为了连通两个点,某条路径可能经过其它路径的中间点。比如1->3->4,2->4->5。但是如果两个点a和b是连通的,只不过中间需要经过其它的点,那么可以在这两个点之间加边,那么a就可以直达b,不必经过中点的,那么就转化成了最小不相交路径覆盖。
void floyd(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
if(dis[i][k]&&dis[k][j]){
dis[i][j]=true;
}
}
}
}
}
模板:poj2594http://poj.org/problem?id=2594