Link:题目
题目大意
我们需要做的,就是找到一种连边的方式,使得任意一个点的度最多为1,且边数最大。
给出一组数据:
input: output:
3 4 5 3
1 1
1 2
2 1
1 4
3 3
此时,方案大致是这样的(当然还有其他选法)。
我们是如何找出这种匹配方式的呢?此时,就要引入匈牙利算法。
匈牙利算法流程
1.首先枚举一个节点的所有边,如果当前边对应的节点不在匹配序列,则加入对应节点,返回匹配成功。
2.如果当前没有匹配成功,且对应点在之前的匹配序列(不在当前),则重新匹配对应点的对应点,执行1。
3.执行完1,2,均未匹配成功,返回失败。
下面我们来模拟一下样例数据。
首先这是原图
搜索左边第一个节点,找到一个没有匹配过的节点,加入匹配序列1。
搜索第二个节点,找到一个匹配过的节点,但不在匹配序列2,重新搜索节点1...,最后返回成功。
搜索第三个节点,找到可以匹配的节点,返回成功。
算法结束。
代码
最后附上代码:
#include<stdio.h>
#define N 1007
struct E{
int next,to;
}e[N*N];
int cnt=0,head[N],n,m,num,timeq=0,dfn[N],match[N],ans=0;
inline void read(int &x){
x=0;char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
}
inline void add(int id,int to){//前向星存图
cnt++;
e[cnt].next=head[id];
e[cnt].to=to;
head[id]=cnt;
}
inline bool dfs(int u){//算法核心
for(int i=head[u];i;i=e[i].next){//遍历
int v=e[i].to;
if(dfn[v]==timeq) continue;//如果已在当前匹配序列,则直接返回
dfn[v]=timeq;//加入当前匹配序列
if(!match[v]||dfs(match[v])){//如果没有匹配过,或者重新搜索对应点返回成功
match[v]=u;//匹配
return true;//匹配成功
}
}
return false;//匹配失败
}
int main(){
read(n);read(m);read(num);
for(int i=1;i<=num;i++){
int x,y;
read(x);read(y);
// if((x>n)||(y>m)) continue;
add(x,y);//建立单向边就够了,因为不会从右边的节点开始访问
}
for(int i=1;i<=n;i++){
timeq++;//更新序列编号
if(dfs(i)) ans++;//搜索当前节点
}
printf("%d",ans);
}