二分图的最大匹配——最大流EK算法

序:
既然是个图,并且求边数的最大值。那么这就可以转化为网络流的求最大流问题
只需要将源点与其中一子集的所有节点相连汇点与另一子集的所有节点相连,将所有弧的流量限制置为1,那么最大流 == 最大匹配。(感谢yulemao大神的指点)

只需要在初始化的时候修改一下,就可以直接用求最大流的算法模板了。
本文代码使用EK算法, 为POJ 1469的AC代码。
EK算法解析


源代码:

/*
About: 二分图最大匹配_网络流EK算法 
2017/04/22
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string.h> 
using namespace std;
#define INF 0x3f3f3f
#define maxm 200005
#define maxn 10005

struct Edge{
    int st, en, num;
    Edge(){}
    Edge(int s, int e, int n):
        st(s), en(e), num(n){}
}flow[maxm];

int n, m;
int st = maxn-2, en = maxn-1;
int pre[maxn], re[maxn][maxn/10], num[maxn];
int q[maxn], curr_pos, st_pos, end_pos, ne, max_flow;
bool wh[maxn];
int cur;

int input;

char *X, *Buffer, c;

void Get_All()
{
    long long file_lenth;
    fseek(stdin, 0, SEEK_END);
    file_lenth = ftell(stdin);
    rewind(stdin);
    Buffer = (char*)malloc(1*file_lenth);
    fread(Buffer,1, file_lenth, stdin);
    X = Buffer;
    return ;
}

int Get_Int()
{
    c = *X;
    input = 0;
    while(c < '0' || c > '9')   c = *++X;
    while(c >= '0' && c <= '9')
    {
        input = input*10+c-'0';
        c = *++X;
    }
    return input;
}

void Add_Edge(int st, int en)
{
    flow[cur] = Edge(st, en, 1);
    flow[cur^1] = Edge(en, st, 0);
    re[flow[cur].st][++num[flow[cur].st]] = cur;
    re[flow[cur].en][++num[flow[cur].en]] = cur^1;
    cur += 2;
    return ;
} 

void Init()
{
    cur = 0;
    memset(num, -1, sizeof num);
    max_flow = 0;
}

static void Read()
{
    int a, nums;
    n = Get_Int(), m = Get_Int();
    for(unsigned i = 0; i != n; ++i)
    {
        Add_Edge(st, i);
        nums = Get_Int();
        for(unsigned j = 0; j != nums; ++j)
        {
            a = Get_Int();
            Add_Edge(i, a+n);
        }
    }
    for(unsigned j = 1; j != m+1; ++j)
    {
        Add_Edge(j+n, en);
    }
    return ;
}

static bool Bfs(int st, int en)
{
    int i, j;
    st_pos = -1, end_pos = 0;
    memset(wh, 0, sizeof wh);
    wh[st] = 1;
    q[0] = st;
    while(st_pos != end_pos)
    {
        curr_pos = q[++st_pos];
        for(i = 0; i < num[curr_pos]+1; ++i)
        {
            j = re[curr_pos][i];
            if(flow[j].st == curr_pos && flow[j].num > 0 && !wh[flow[j].en])
            {
                ne = flow[j].en;
                wh[ne] = 1;
                pre[ne] = j;
                q[++end_pos] = flow[j].en;
                if(ne == en)    return true;         
            }
        }
    }
    return false;
} 

void EK(int start_pos, int end_pos)
{
    int i, minn;
    while(Bfs(start_pos, end_pos))
    {
        minn = INF;

        for(i = end_pos; i != st; i = flow[pre[i]].st)
        {
            minn = min(minn, flow[pre[i]].num);
        } 

        for(i = end_pos; i != st; i = flow[pre[i]].st)
        {
            flow[pre[i]].num -= minn;
            flow[pre[i]^1].num += minn;
        } 
        max_flow += minn;
    }
    return ;
}

void Print()
{
    if(max_flow == n)
    {
        cout << "YES" << endl; 
    }
    else
    {
        cout << "NO" << endl;
    }
    return ;
}

int main()
{
    freopen("test.in", "r", stdin);

    Get_All();
    int times = Get_Int();
    while(times --> 0)
    {
        Init();
        Read();
        EK(st, en);
        Print();
    }

    fclose(stdin);
    return 0;
}

需要注意的是,在实测中,上一篇文章所使用的匈牙利算法耗时70+ms,但是本文代码耗时600+ms
所以利用最大流算法计算二分图的最大匹配只是借鉴思路若不是有特殊原因,建议不使用


至此结束。
箜瑟_qi 2017.04.22 16:02

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值