人员分配
Description
设有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
解题思路
这题就是一道最大匹配
是匈牙利算法和网络流的模板题
我初学,只学了匈牙利算法
所以只写了匈牙利算法(抱歉)
它的基本思想是:对于已知的匹配M,从X中的任一选定的M非饱和点出发,用标号法寻找M增广链。如果找到M增广链,则M就可以得到增广;否则从X中另一个M非饱和点出发,继续寻找M增广链。重复这个过程直到G中不存在增广链结束,此时的匹配就是G的最大匹配。这个算法通常称为匈牙利算法,因为这里介绍的寻找增广链的标号方法是由匈牙科学者Egerváry最早提出来的。
由增广路的定义可以推出下述三个结论:
1-P的路径长度必定为奇数,第一条边和最后一条边都不属于M。
2-P经过取反操作可以得到一个更大的匹配M’。
3-M为G的最大匹配当且仅当不存在相对于M的增广路径。
算法轮廓:
(1)置M为空
(2)找出一条增广路径P,通过取反操作获得更大的匹配M’代替M
(3)重复(2)操作直到找不出增广路径为止
上面的图为 :二分图
二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,{R})是一个无向图。如顶点集V可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属两个不同的子集。则称图G为二分图。
二分图
AC代码
邻接矩阵
邻接矩阵就很简单了,用一个a来存相连的边,再套用匈牙利算法就AC了
#include<iostream>
#include<cstdio>
using namespace std;
int m,n,s,x,y,answer,cover[105],father[105],a[105][105];
bool dfs(int x)//dfs
{
if(x==0)return true;//特判,节省时间
for(int i=1;i<=n;i++)
if(a[x][i]==1&&cover[i]==0)//如果有边,且未被标记
{
cover[i]=1;//标记
int sum=father[i];//在dfs内int,不能在外面int,否则sum会成为全局变量
father[i]=x;//更改
if(sum==0||dfs(sum))return true;
father[i]=sum;//回溯
}
return false;//返回
}
int main()
{
scanf("%d%d",&m,&n);//输入
scanf("%d",&s);
for(int i=1;i<=s;i++)
{
scanf("%d%d",&x,&y);
a[x][y]=1;//相连边标记
}
for(int i=1;i<=m;i++)//每个点都枚举一次
{
memset(cover,0,sizeof(cover));//清零
dfs(i);
}
for(int i=1;i<=n;i++)//累加
if(father[i]!=0)answer++;
printf("%d",answer);//输出
return 0;
}
邻接表
邻接表就和邻接矩阵的方法差不多,同样用一个a来存储相连的边,只不过存储的方式不同,再套用匈牙利算法就AC了
邻接表(不会的请进)
#include<iostream>
#include<cstdio>
using namespace std;
int m,n,s,x,y,tot,answer,head[105],cover[105],father[105];
struct node//结构体
{
int to,next;
}a[10005];
void add(int x,int y)//邻接表
{
a[++tot]=(node){y,head[x]};
head[x]=tot;
}
bool dfs(int x)//dfs
{
if(x==0)return true;//特判,节省时间
for(int i=head[x];i;i=a[i].next)//枚举相连的边
if(cover[a[i].to]==0)//如果没有被标记
{
cover[a[i].to]=1;//标记
int sum=father[a[i].to];//在dfs里面int,在外面int会变成全局变量
father[a[i].to]=x;//更改
if(sum==0||dfs(sum))return true;
father[a[i].to]=sum;//回溯
}
return false;//返回
}
int main()
{
scanf("%d%d",&m,&n);//输入
scanf("%d",&s);
for(int i=1;i<=s;i++)
{
scanf("%d%d",&x,&y);
add(x,y);//建表
}
for(int i=1;i<=m;i++)//枚举每一个工人
{
memset(cover,0,sizeof(cover));//清零
dfs(i);
}
for(int i=1;i<=n;i++)//累加
if(father[i]!=0)answer++;
printf("%d",answer);
return 0;
}