有向无环图(DAG)的最小路径覆盖

http://www.cnblogs.com/justPassBy/p/5369930.html

http://www.cnblogs.com/ka200812/archive/2011/07/31/2122641.html

定理:
柯尼希定理:二分图最小点覆盖的点数=最大匹配数。
最小路径覆盖的边数=顶点数n-最大匹配数
最大独立集=最小路径覆盖=顶点数n-最大匹配数

增广路定理:用未盖点表示不与任何匹配边邻接的点,其他点位匹配点,即恰好和一条匹配边临界的点。从未盖点出发,依次经过非匹配边,匹配边,非匹配边,匹配边。。。所得到的路径称为交替路。注意,如果交替路的终点时一个未盖点,则称这条交替路位一条增广路。在增广路中,非匹配边比匹配边多一条。增广路的作用是改进匹配。如果有一条增广路,那么把此路上的匹配边和非匹配边互换,得到的匹配比刚才多一边。反过来,如果找不到增广路,则当前匹配就是最大匹配。

查找增广路,存在增广路就交换增广路上的非匹配边和匹配边,这样会使得当前最大匹配数+1。

DAG的最小路径覆盖

 

定义:在一个有向图中,找出最少的路径,使得这些路径经过了所有的点。

最小路径覆盖分为最小不相交路径覆盖最小可相交路径覆盖

最小不相交路径覆盖:每一条路径经过的顶点各不相同。如图,其最小路径覆盖数为3。即1->3>4,2,5。

最小可相交路径覆盖:每一条路径经过的顶点可以相同。如果其最小路径覆盖数为2。即1->3->4,2->3>5。

特别的,每个点自己也可以称为是路径覆盖,只不过路径的长度是0。

 

DAG的最小不相交路径覆盖

算法:把原图的每个点V拆成VxVxVyVy两个点,如果有一条有向边A->B,那么就加边Ax>ByAx−>By。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数-新图的最大匹配数。

证明:一开始每个点都是独立的为一条路径,总共有n条不相交路径。我们每次在二分图里找一条匹配边就相当于把两条路径合成了一条路径,也就相当于路径数减少了1。所以找到了几条匹配边,路径数就减少了多少。所以有最小路径覆盖=原图的结点数-新图的最大匹配数。

因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。

习题:POJ1422

//
//  main.cpp
//  POJ1422最小不想交路径覆盖
//
//  Created by beMaster on 16/4/8.
//  Copyright © 2016年 beMaster. All rights reserved.
//

#include 
#include 
#include 
#include 
using namespace std;
const int N = 200 + 10;
vector g[N];
int cy[N];
bool vis[N];
bool dfs(int u){
    for(int i=0; i

DAG的最小可相交路径覆盖

算法:先用floyd求出原图的传递闭包,即如果a到b有路径,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。

证明:为了连通两个点,某条路径可能经过其它路径的中间点。比如1->3->4,2->4->5。但是如果两个点a和b是连通的,只不过中间需要经过其它的点,那么可以在这两个点之间加边,那么a就可以直达b,不必经过中点的,那么就转化成了最小不相交路径覆盖。

题目:POJ2594

//
//  main.cpp
//  POJ2594最小可相交路径覆盖
//
//  Created by beMaster on 16/4/8.
//  Copyright © 2016年 beMaster. All rights reserved.
//

#include 
#include 
#include 
#include 
using namespace std;
const int N = 500 + 10;
bool dis[N][N];
bool vis[N];
int cy[N];
void floyd(int n){
    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;
}
bool dfs(int u, int n){
    for(int i=1;i<=n;++i){
        if(!vis[i] && dis[u][i]){
            vis[i] = true;
            if(cy[i]==-1 || dfs(cy[i], n)){
                cy[i] = u;
                return true;
            }
        }
    }
    return false;
}
int solve(int n){
    int cnt = 0;
    memset(cy,-1,sizeof(cy));
    for(int i=1;i<=n;++i){
        memset(vis,0,sizeof(vis));
        cnt += dfs(i, n);
    }
    return n - cnt;
}
int main(int argc, const char * argv[]) {
    int n,m;
    int a,b;
    while(scanf("%d%d",&n,&m),n+m){
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                dis[i][j] = false;
        for(int i=1;i<=m;++i){
            scanf("%d%d",&a,&b);
            dis[a][b] = true;
        }
        floyd(n);
        int ans = solve(n);
        printf("%d\n",ans);
    }
    return 0;
}

最小路径覆盖问题值得注意的问题

首先,最小路径覆盖=总节点数-最大匹配数。这个应该已经是路人皆知了。

所谓最小路径覆盖,是指在一个有向图中,找出最少的几条路径,用它们来覆盖全图

这里说的值得注意的地方,如果有向图的边有相交的情况,那么就不能简单的对原图求二分匹配了

举个例子,假设有图:1->2    2->5     2->3      4->2,事实上,这其实就是两条边:1->5 4->3 ,节点2只是他们的一个交点


有向无环图(DAG)的最小路径覆盖_第1张图片

如果只是简单的在原图的基础上求二分匹配,那么得到的匹配答案是2,最小路径覆盖答案便是5-2=3。

可是随便一看都能看看出端倪,这个图中,只需要两个点便可以探索完整个地图,这里最小路径覆盖数明显是2。

问题究竟出在哪里呢?其实就和这个交点2有关。既然边有相交,那么他们的连通性也应该连通下去。

解决的办法是对原图进行一次闭包传递(也就是flody),于是便增加了四条边:1->3  1->5   4->3  4->5

这时再求最大匹配数,匹配答案便是3,最小路径覆盖值为2,这是正确答案!

你可能感兴趣的:(模板)