背景知识:
什么是二分图?
二分图就是一种特殊的图。这个图可以将整个集合分成了两个子集,满足所有弧都是从其中一子集的顶点射向另一个子集的顶点。同一个子集中的点没有弧相连。
二分图的最大匹配:
二分图的最大匹配就是使两个集合的点尽可能多的一一对应。
本次介绍的是匈牙利算法。
匈牙利算法的原理就是寻找增广路径以扩大匹配数量。与最大流增广算法原理基本一致。
增广路径:
若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