二分图匹配

一.最大匹配
匈牙利算法:
总体来说,就是不断找增广路,如果找到 i 可以匹配而没有匹配的点 j ,就将这个点 j 与 i 配对,而如果发现要匹配的点 j 已经和 k 配对了,就对这个点 j 所配对的点 k 进行寻找增广路的操作,看这个点k 能不能另外找到其他的点 x 来替代 j 点来进行配对,如果可以,就用 x来代替 j 和k 进行配对,j 和 i来配对,即可。如果找不到,那么就用另外的点来和 i 进行配对尝试。以此类推。
匈牙利算法建的是单向边
代码1:(建双向边):

#include <bits/stdc++.h>
using namespace std;//不断妥协,不断找增广路
const int N=1e3+5;
vector<int>pic[N];//邻接表存图
int match[N];//所匹配的顶点
bool vis[N];//dfs的访问标记
int k,n,m;
void add_edge(int u,int v)
{
    pic[u].push_back(v);
    pic[v].push_back(u);
}
bool dfs(int v)//dfs找增广路
{
    vis[v]=true;
    for(int i=0;i<pic[v].size();i++)
    {
        int u=pic[v][i];
        int w=match[u];
        if(w<0||!vis[w]&&dfs(w))//要被配对的点,要么还没有配对,要么已经配对,但可以妥协(让和它配对的与能够配对的配对)
        {//匈牙利树,交替路,稀疏图bfs更快,稠密图接近
            match[v]=u;
            match[u]=v;
            return true;
        }
    }
    return false;
}
//求解二分图的最大匹配
int b_matching()
{
    int res=0;
    memset(match,-1,sizeof(match));
    for(int i=1;i<=m+n;i++)
    {
        if(match[i]<0)
        {
            memset(vis,0,sizeof(vis));
            if(dfs(i))
                res++;
        }
    }
    return res;
}
int main()
{
     while(scanf("%d",&k),k)
     {
         scanf("%d%d",&m,&n);//女生1~m,男生m+1~m+n
         int a,b;
         for(int i=1;i<=k;i++)
         {
             scanf("%d%d",&a,&b);
             add_edge(a,b+m);
         }
         printf("%d\n",b_matching());
     }
    return 0;
}

代码2:(HDU 2063)
当把点分成两份进行匹配,只遍历一边时,建单向边
如果不分图,遍历全部点时,要建立双向边,最后最大匹配结果要除2
匈牙利算法模板,建有向边。

#include<cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int N=1100;
int k,n,m,V;
int match[N];
bool vis[N];
vector<int>pic[N];
bool dfs(int u)
{
    for(int i=0;i<pic[u].size();i++)
    {
        int v=pic[u][i];
        if(!vis[v])//走未匹配的边
        {
            vis[v]=true;
            int w=match[v];
            if(w<0||dfs(w))//dfs(w):走已匹配的边
            {
                match[v]=u;
                return true;
            }
        }
    }
    return false;
}
int b_match()
{
    int ans=0;
    memset(match,-1,sizeof(match));
    for(int i=1;i<=V;i++)
    {
        memset(vis,false,sizeof(vis));
        if(dfs(i))
            ans++;
    }
    return ans;
}
int main()
{
    while(scanf("%d",&k),k)
    {
        scanf("%d%d",&m,&n);
        V=n+m;
        for(int i=1;i<=V;i++)
            pic[i].clear();
        int a,b;
        for(int i=1;i<=k;i++)
        {
            scanf("%d%d",&a,&b);
            pic[a].push_back(b+m);
        }
        printf("%d\n",b_match());
    }
    return 0;
}

.最小顶点覆盖
即用最少的点去覆盖所有的边。
先引入一个定理:
könig定理:最小顶点覆盖=最大匹配
因此求最小顶点覆盖就可以转化为求最大匹配,直接用模板就行。难点在于如何建立最小顶点覆盖的模型,常见的建图方法有行列建图
(目前就遇见过这种),但根本在于如何根据问题性质去转化,找到对应的匹配关系,一般是通过目标把两个集合联系起来,建立匹配关系。
大致过程如下:
1.从右边找到一个未被匹配的点,标记。
2.走一条没被匹配的边,到左边的点,标记。
3.走一条匹配过的边到右边,标记。
4.重复2.3步骤直到不能再走。
5.回到步骤1,直到找不到未被匹配且未被标记的右边的点。
6.标记结束后,右边没有标记的点,和左边已经标记的点,就可以覆盖所有的边。
相关题目:
*
POJ 3041 POJ 2446 POJ1486 POJ 1325 POJ 3020 HDU 1054
POJ 2226(巧妙建图)UVA 11419(*)
交替路:
从一个未匹配的点出发,依次经过非匹配边,匹配边,非匹配边,匹配边…形成的路径。
增广路:
从一个未匹配的点出发,走交替路,如果途径一个未匹配的点,(出发点不算),那么,这条路就叫做增广路。特点:非匹配边=匹配边+1
匈牙利算法就是走增广路,直到没有。
匈牙利树:

.最小路径覆盖
DAG图中(有向无环图):
最小路径覆盖=顶点数-最大匹配
不可相交的最小路径覆盖:
代码(HDU 1151):

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int N=130;
int n,m;
vector<int>pic[N];
int match[N];
bool vis[N];
bool dfs(int u)
{
    for(int i=0;i<pic[u].size();i++)
    {
        int v=pic[u][i];
        if(!vis[v])
        {
            vis[v]=true;
            int w=match[v];
            if(w<0||dfs(w))
            {
                match[v]=u;//存每个终点对应的起点
                //match[u]=v;
                return true;
            }
        }
    }
    return false;
}
int b_match()
{
    int ans=0;
    memset(match,-1,sizeof(match));
    for(int i=1;i<=n;i++)
    {
        memset(vis,false,sizeof(vis));
        if(dfs(i))
            ans++;
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            pic[i].clear();
        int a,b;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            pic[a].push_back(b);
        }
        printf("%d\n",n-b_match());
        //for(int i=1;i<=n;i++)printf("%d:%d\n",i,match[i]);
    }
    return 0;
}

相关题目:
HDU 3335(最大独立集)
HDU 6311(*最小路径覆盖+路径输出)
HDU 4160
UVA 11419
POJ 2594(可相交的最小路径覆盖)

可相交的最小路径覆盖问题:
允许点重复走,即两点之间可以间接到达,不能直接建图跑二分图匹配。先用 floyd传递闭包,把间接到达改成直接到达,然后根据得到的邻接矩阵来建图。
最小路径覆盖=顶点数-最大二分匹配

*.*最大独立集和最大团:
详解+模板
独立集:图中互不相连的顶点集合
最大独立集+最小顶点覆盖=顶点数
可以通过最大匹配求出最大独立集的数目
求最大独立集是一个NP难问题,目前没有多项式时间内的解法,所以常见解法就是搜索。
如果仅仅要求求出最大独立集的元素个数,可以直接用最大独立集和最大匹配的关系,跑一遍匈牙利算法即可。
如果要求输出最大独立集的元素,则要转化为最大团的问题,一个图的最大团=补图的最大独立集。
*.最小边覆盖
无孤立点的图中,最大匹配+最小边覆盖=顶点数

完备匹配:

最佳匹配;

最佳完备匹配

多重匹配:
可以用网络流写

带权匹配:
可以用费用流
**二分图的最佳完备匹配(**KM算法):
HDU 2255(用最大流最大费用可以解决)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值