题目
设有M个工人x1, x2, …, xm,和N项工作y1, y2, …, yn,规定每个工人至多做一项工作,而每项工作至多分配一名工人去做。由于种种原因,每个工人只能胜任其中的一项或几项工作。问应怎样分配才能使尽可能多的工人分配到他胜任的工作。这个问题称为人员分配问题。
Input
第一行两个整数m,n分别为工人数和工作数。
接下来一个整数s,为二分图的边数。
接下来s行,每行两个数ai,bi表示第ai个工人能胜任第bi份工作
Output
一个整数,表示最多能让多少个工人派到自己的胜任的工作上。
Sample Input
3 3
4
1 2
2 1
3 3
1 3
Sample Output
3
Hint
规模:
1<=m,n<=100
1<=s<=10000
最大匹配初步解析
第一题最大匹配,好激动啊!
这道是练手的模板题。。。
换一个背景,有n朵花,m片叶子,每朵花有它可以搭配的叶子,且每朵花只能搭配一片叶子,求最多能让多少朵花分配到与自己相配的叶子。
3 3
4
1 2
2 1
3 2
1 3
用cover[i]表示每片叶子是否已被匹配过,link[i]表示每片叶子与之匹配的花的编号
先从第一朵花做起,发现第一朵花与第二片叶子匹配,则cover[2]标记,link[2]记为1,表现在图上,就是:
第一朵花匹配好了,就往下,轮到第二朵花,发现第二朵花与第一片叶子匹配,且第一片叶子没有与之匹配的花,则cover[1]标记,link[1]记为2,表示第一片叶子与第二朵花匹配了,表现在图上,就是:
接下来轮到第三朵花,发现第三朵花可以与第二片叶子匹配,但是第二片叶子已经名“花”有主了,注意,这时就先取消掉第一朵花与第二片叶子的匹配,并且把第三朵花与第二片叶子匹配,如图:
然后尝试重新给第一朵花找叶子,注意,这一步表现在代码上为递归实现find(1),然后找可以与第一朵花匹配的叶子。先找到第二片叶子,但是cover[2]已经标记过了,那就继续往下找,发现第一朵花还可以与第三片叶子匹配,所以就把第一朵花与第三片叶子匹配,返回true,如图:
现在第一朵花名花有主了,可以返回到第三朵花的匹配,匹配成功,返回主程序。匹配结果如图,淡蓝色粗线即为匹配成功:
表现为代码如下:
注意,在从主程序进入find时,cover要清空
function find(i:longint):boolean;
var
q,k:longint;
begin
find:=true;
q:=0;
for k:=1 to m do
if map[i,k] and not cover[k] then
begin
q:=link[k]; //先记录叶子k刚开始的匹配花编号,若用花i与之匹配不成功则叶子k仍如花q匹配
link[k]:=i; //表示叶子k与花i匹配
cover[k]:=true;//标记叶子k的cover
if (q=0)or(find(q)) then exit;//如果叶子k一开始就是没有匹配的,或花q可以找到别的匹配,则匹配成功,返回true
link[k]:=q;//匹配不成功,叶子k仍与花q匹配
end;
find:=false;
end;
补充一下,最大匹配是
O
(
n
3
)
O(n^3)
O(n3)
然后难点在建图而不是匹配本身
对于建好的二分图,用有向边从固定的一边连向另一边;
对于边的储存,用邻接图和链表(前向星)均可;
当然,这只是最大匹配初步,关于最大匹配还有很多衍生问题,有待探究。
代码
var
n,m,s,i,j,k,ans:longint;
cover:array[1..100]of boolean;
link:array[1..100]of longint;
map:array[1..100,1..100]of boolean;
function find(i:longint):boolean;
var
q,k:longint;
begin
find:=true;q:=0;
for k:=1 to m do
if map[i,k] and not cover[k] then
begin
q:=link[k];
link[k]:=i;
cover[k]:=true;
if (q=0)or(find(q)) then exit;
link[k]:=q;
end;
find:=false;
end;
begin
readln(n,m);
readln(s);
for i:=1 to s do
begin
readln(j,k);
map[j,k]:=true;
end;
for i:=1 to n do
begin
fillchar(cover,sizeof(cover),false);
find(i);
end;
for i:=1 to m do
if link[i]>0 then inc(ans);
writeln(ans);
end.
现在,我们再来一个C++的
#include <cstdio>
#include <cstring>
using namespace std;
int n,m,s,ans;
int ls[105],ne[10004],y[10004],link[105];
bool cover[105];
bool dfs(int k){
for (int i=ls[k];i;i=ne[i])
if (!cover[y[i]]){
int t=link[y[i]];
link[y[i]]=k;
cover[y[i]]=1;
if (t==0||dfs(t)) return true;
link[y[i]]=t;
}
return false;
}
int main(){
scanf("%d%d",&n,&m);
scanf("%d",&s);
for (int i=1;i<=s;i++){
int a,b;
scanf("%d%d",&a,&b);
ne[i]=ls[a];ls[a]=i;y[i]=b;
}
for (int i=1;i<=n;i++){
memset(cover,0,sizeof(bool)*102);
dfs(i);
}
for (int i=1;i<=n;i++)
if (link[i]) ans++;
printf("%d\n",ans);
}