二分图匹配 --- 最小路径覆盖

结论 : DAG的最小路径的最小路径覆盖 = 顶点数 - 对应二分图最大匹配数(单点也算一条路径)
解释: 最小路径覆盖 : 在图中选取尽量少的路径. 使得每个结点恰好在一条路径上(换句话说, 不同的路径不能有公共点).
//如果是无向图, 建的双向边, 所以无向图中的最小路径覆盖 = 顶点数 - 对应二分图最大匹配数/2(被算了两次)不过这个很少用, 一般单向就够了.
//还有就是对于有环的单向图这个结论就不适用, heihei, 自己想想呀.!

//网上看了很多博客, 我认为的是没有一个讲好了的. 全部都是直接带出结论, 所以我也是很迷茫.但是证明还是有一点难的. (注意这里的路径可以是一条很长的路, 并不是只代表了某一条边)
我认为的最通俗易懂的理解方法就是 :
假设原图中没有边, 那么最小路径数就是等于顶点数, 二分图匹配数加1, 则就在原图中加上了一条边, 对应的路径数就-1, 因为二分图中的边就是在原图中连的一条边. 这样一直下去(大牛说是数学归纳法), 就可以求得最小的路径数了. (是不是说得还是有一点迷糊, 我只能说到这个地步了)

那么就具体说说一些题目 :

HDU — 1151
//题意n个 路口, m条单向路, 问最少需要多少个伞兵才能把所有的路口的遍历完.
//这道题就是一道裸最小路径覆盖, 直接建边, 跑一跑二分图, 套一套结论就行了.

AC Code

const int maxn=150+5;
bool g[maxn][maxn],vis[maxn];
int link[maxn];
int n,m;
bool dfs(int x)
{
    for(int i=1;i<=n;i++){
        if(vis[i] || !g[x][i]) continue;
        vis[i] = true;
        if(link[i] == -1 || dfs(link[i])){
            link[i] = x;
            return true;
        }
    }
    return false;
}
void solve()
{
    Fill(g,false);
    scanf("%d%d",&n,&m);
    for(int i=0;iint u,v;
        scanf("%d%d",&u,&v);
        g[u][v] = true;    //有向边.
    }
    int res = 0;
    Fill(link,-1);
    for(int i=1;i<=n;i++){
        Fill(vis,false);
        if(dfs(i)) res++;
    }
    printf("%d\n",n-res);
}

poj — 2060
//题意 : 给你m个人的出发时间和每一个人起点和终点, 保证出发时间以递增顺序给出. 问最少需要几辆车才能满足所有人的日程. 注意每一辆车必须在要搭载的人的出发时间之前至少一分钟之前到达.
//思路: 那么, 对于每一个要车的人作为一个点, 那么如果当前点的车在弄好了以后, 可以到达在下一个点出发之前到达, 那么他们两个之间就有一条边. 然后求一下这个图的最小路径覆盖就行了.

AC Code

const int maxn=500+5;
bool g[maxn][maxn],vis[maxn];
int link[maxn];
int n;
struct node
{
    int t,x1,y1,x2,y2;
}s[maxn];

bool dfs(int x)
{
    for(int i=1;i<=n;i++){
        if(vis[i] || !g[x][i]) continue;
        vis[i] = true;
        if(link[i] == -1 || dfs(link[i])){
            link[i] = x;
            return true;
        }
    }
    return false;
}

void solve()
{
    scanf("%d",&n);
    Fill(s,0); Fill(g,false); Fill(link,-1);
    for(int i=1;i<=n;i++){
        int a,b,x1,y1,x2,y2;
        scanf("%d:%d%d%d%d%d",&a,&b,&s[i].x1,&s[i].y1,&s[i].x2,&s[i].y2);
        s[i].t = a*60+b;
    }

    for(int i=1;i<=n;i++){   //一一判断任意两点之间是否可以由一辆
    //车到达.有就有一条边,否则就没有.
        for(int j=i+1;j<=n;j++){
            int dis1 = abs(s[i].x1-s[i].x2)+abs(s[i].y1-s[i].y2);
            int dis2 = abs(s[i].x2-s[j].x1)+abs(s[i].y2-s[j].y1);
            if(s[j].t-1>=s[i].t+dis1+dis2) g[i][j] = true;
        }
    }

    int res = 0;
    for(int i=1;i<=n;i++){
        Fill(vis,false);
        if(dfs(i)) res++;
    }

    printf("%d\n",n-res);
}

你可能感兴趣的:(二分图)