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

序:
既然是個圖,而且求邊數的最大值。那麼這就能夠轉化爲網絡流的求最大流問題
只須要將源點與其中一子集的全部節點相連匯點與另外一子集的全部節點相連,將全部弧的流量限制置爲1,那麼最大流 == 最大匹配。(感謝yulemao大神的指點)ios

只須要在初始化的時候修改一下,就能夠直接用求最大流的算法模板了。
本文代碼使用EK算法, 爲POJ 1469的AC代碼。
EK算法解析算法


源代碼:markdown

/* 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:02ui

相關文章
相關標籤/搜索