二分图最大匹配

讲解:

http://www.renfei.org/blog/bipartite-matching.html

(该博客最大独立集)

补充定义和定理:

最大匹配数:最大匹配的匹配边的数目

最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择

最大独立数:选取最多的点,使任意所选两点均不相连

最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。

定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)

定理2:最大独立数 = 顶点数 - 最大匹配数 

定理3:最小路径覆盖数 = 顶点数 - 最大匹配数


手工模板:

http://paste.ubuntu.com/12192113/


//#include<bits/stdc++.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <map>
#include <queue>
#include <stack>
#include <set>
#include <algorithm>

using namespace std;

#define For(i,a,b) for(int (i)=(a);(i) < (b);(i)++)
#define rof(i,a,b) for(int (i)=(a);(i) > (b);(i)--)
#define IOS ios::sync_with_stdio(false)
#define lson l,m,rt <<1
#define rson m+1,r,rt<<1|1
#define mem(a,b) memset(a,b,sizeof(a))

typedef long long ll;
typedef unsigned long long ull;


void RI (int& x){
    x = 0;
    char c = getchar ();
    while (c == ' '||c == '\n')    c = getchar ();
    bool flag = 1;
    if (c == '-'){
        flag = 0;
        c = getchar ();
    }
    while (c >= '0' && c <= '9'){
        x = x * 10 + c - '0';
        c = getchar ();
    }
    if (!flag)    x = -x;
}
void RII (int& x, int& y){RI (x), RI (y);}
void RIII (int& x, int& y, int& z){RI (x), RI (y), RI (z);}

/**************************************END define***************************************/
const int maxn = 2e3+10;//单侧顶点最大数目
const int INF =0x3f3f3f3f;
const int maxm = 2e4+10;
struct Side
{
    int from,to,next;
};
struct BPM{
    int n,m;    //左右n,m个点
    Side side[maxm];
    int top;
    int node[maxn];
    int left[maxn];       // left[i]为右边第i个点的匹配点编号,-1表示不存在
    bool T[maxn];           // T[i]为右边第i个点是否已标记

    /*求最小覆盖点集时用
    int right[maxn];    // right[i]为左边第i个点的匹配点编号,-1表示不存在
    bool S[maxn];       //左边第i个点,被标记为true时:为未盖点
    */

    void init(int nn,int mm)
    {
        n=nn,m=mm;
        top=0;
        mem(node,-1);
    }
    void add_side(int u,int v)
    {
        side[top]=(Side){u,v,node[u]};  node[u]=top++;
    }
    bool dfs(int u){
        /*S[u]=true;*/
        for(int i=node[u];i!=-1;i=side[i].next){
            int v=side[i].to;
            if(!T[v]){
                T[v]=true;
                if(left[v]==-1||dfs(left[v])){
                    left[v]=u;
                    /*right[u]=v;*/
                    return true;
                }
            }
        }
        return false;
    }

    //求最大匹配,匈牙利算法
    int hungarian()
    {
        int ans=0;
        mem(left,-1);
        /*mem(right,-1);*/
        // u 范围为0~n-1
        for(int u=0;u<n;u++){
            /*mem(S,0);*/
            mem(T,0);
            if(dfs(u)) ans++;
        }
        return ans;
    }
    /*
    //求最小覆盖。X和Y为最小覆盖中的点集
    int mincover(vector<int>& X,vector<int>& Y){
        int ans=hungarian();
        mem(S,0);
        mem(T,0);
        for(int u=0;u<n;u++)
            if(right[u]==-1) dfs(u);// 从所有X未盖点出发增广
        for(int u=0;u<n;u++)
            if(!S[u]) X.push_back(u);//S[u]为false为匹配点
        for(int v=0;v<m;v++)
            if(T[v])Y.push_back(v);//T[u]为true为匹配点
        return ans;
    }
    */
};
BPM solver;
int main()
{
    freopen("input.txt","r",stdin);
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        solver.init(n,n);
        while(m--){
            int x,y;
            RII(x,y);
            x--;y--;
            solver.add_side(x,y);
        }
        int ans=solver.hungarian();
        printf("%d\n",ans);
    }

    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值