二分图的最大匹配——匈牙利算法

背景知识:
什么是二分图?
二分图就是一种特殊的图。这个图可以将整个集合分成了两个子集,满足所有弧都是从其中一子集的顶点射向另一个子集的顶点同一个子集中的点没有弧相连

二分图的最大匹配:
二分图的最大匹配就是使两个集合的点尽可能多一一对应

本次介绍的是匈牙利算法

匈牙利算法的原理就是寻找增广路径以扩大匹配数量。与最大流增广算法原理基本一致。

增广路径:
若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。(百度百科)

源代码较长的原因是1.读入优化2.边表存储。
源代码的输入格式POJ 1469所描述。


源代码:

/*
About: 二分图最大匹配_匈牙利算法 
From: POJ 1469 
Auther: kongse_qi
Date:2017/04/22
*/
//#include <bits/stdc++.h>/* POJ的编译器不支持该头文件... */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string.h> 
#define maxn 10005 
using namespace std;

struct Edge{
    int from;
    int to;
    int weight;
    Edge(){} //初始化
    Edge(int f, int t, int w):
        from(f), to(t), weight(w){}//读入
};//边表

int times; /* 输入次数(多组数据) */
int n, m, ans; /* 子集A的顶点数与子集B的顶点数 */
vector<Edge> edge;
vector<int> arc[maxn]; /* 存储每个起点所有的边在edge里的编号 */
typedef vector<int>::iterator iterator_t; /* vector的迭代器 */
bool check[maxn]; /* 用于检查是否已经匹配 */
int match[maxn]; /* 对应的匹配节点 */

char *X, *Buffer, c; /* fread读入优化 */
int input; /* 同上 */

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 ;
}

void 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 ;
}

void Init() /* 由于是多组数据,需要将上一次的数据清空 */
{
    edge.resize(0);
    for(unsigned i = 0; i != n; ++i)
    {
        arc[i].resize(0);
    }
    ans = 0;
    return ;
}

void Read() /* 读入数据 */
{
    int cur, nums;
    Get_Int(), n = input;
    Get_Int(), m = input;
    for(unsigned i = 0; i != n; ++i)
    {
        edge.reserve(edge.size()); 
        Get_Int(), nums = input;
        arc[i].resize(nums);
        for(unsigned j = 0; j != nums; ++j)
        {
            Get_Int(), cur = input;
            edge.push_back(Edge(i, cur+n, 0)); 
            /*加n的原因是他的两集合的节点序号都是从1开始,需要分开 */
            /* 如果用网络流做那么还需要将反向弧放入集合 */
            arc[i][j] = edge.size()-1; /*编号 */
        }
    }
    return ;
}

bool Dfs(int cur)
{
    int cur_to;
    for(iterator_t i = arc[cur].begin(); i != arc[cur].end(); ++i)
    /* 遍历所有与cur节点相连的弧 */
    {
        cur_to = edge[*i].to; /* 该弧终点 */
        if(check[cur_to] == false) /* 如果当前未遍历到 */
        {
            check[cur_to] = true; /* 那么此次遍历 */
            if(match[cur_to] == -1 || Dfs(match[cur_to])) //如果当前节点为匹配或者它匹配的节点(递归)未匹配
            {
                match[cur] = cur_to; /* 匹配两节点 */
                match[cur_to] = cur;
                return true; /* 匹配成功,最大匹配数+1 */
            }
        }
    } 
    return false; /* 匹配未成功 */
}

void Match()
{
    memset(match, -1, sizeof match);
    for(unsigned i = 0; i != n; ++i) /* 所有节点均尝试匹配 */
    {
        if(match[i] == -1) /* 如果当前节点未被匹配 */
        {
            memset(check, 0, sizeof check); /* 将上一次匹配数据置空 */
            if(Dfs(i)) /* 如果匹配成功 */
            {
                ++ans;
            }
        }
    } 
    return ;
}

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

    Get_All();
    Get_Int(), times = input;
    while(times --> 0) /* times -= 1直到times == 0 */
    {
        Init();
        Read();
        Match();
        printf("%d\n", ans);
    }

    return 0;
}

关于上文读入优化
关于上文的结构体初始化


自此结束。
箜瑟_qi 2017.04.22 15:47

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值