poj1469【模板】【二分图最大匹配】

该博客介绍了如何运用匈牙利算法解决一个关于学生选课的问题,确保每个学生都能代表一门课并且每门课都有学生代表。文章详细阐述了增广路的概念,以及匈牙利算法在寻找最大匹配过程中的步骤,并提供了具体的算法流程和代码参考。

题意

一共有N个学生跟P门课程,一个学生可以任意选一门或多门课,问是否达成:
1.每个学生代表的都是不同的课(如果一个学生选修的那门课,那么他就可以代表这门课)
2.每门课都有一个代表
输入为:
P N(课程数跟学生数)
接着有P行,格式为Count studenti studenti+1 ……studentcount
(Count表示对课程1感兴趣的学生数,接着有Count个学生)
如第一行2 1 2表示学生1跟学生2对课程1感兴趣
输出为:
若每门课都能找到一位代表则输出”YES”,否则为”NO”

匈牙利算法

增广路

增广路(也称增广轨或交错路): 若P是图G中一条连通两个未匹配顶点的路径,并且属M的边和不属M的边(即匹配边和非匹配边)在P上交替出现,则称P为相对于M的一条增广路径。
在这里插入图片描述
在图中,红边为三条边的匹配。路径x4→y3→x2→y1→x1→y2是一条交错路

由增广路的定义可以推出下述三个结论:
(1).P的路径长度必定为奇数,第一条边和最后一条边都不属于M,因为两个端点分属两个集合,且未匹配。
(2).P经过取反操作可以得到一个更大的匹配M’。
(3).M为G的最大匹配当且仅当不存在相对于M的增广路径。

找最大匹配的匈牙利算法流程

假如我们用xM数组表示左边节点对其右边节点的匹配,
yM表示右边节点对其左边节点的匹配,初始化为-1;
首先我们先看节点1,寻找下一条边,假设找到节点5,因为1跟5都还没匹配,所以找到一个匹配.标记,xM[1]=5,yM[5]=1;
现在重点看节点3,当寻找下一条边时,如图中的蓝边,我们发现节点6的yM[6]=2;已经匹配了.
此时我们就转到节点6的匹配点2上去,发现节点2的另一条边2->5中节点5也已经匹配了,yM[5]=1;继续转到节点1,发现节点1的边1->4中节点4还没匹配.于是我们找到了一个增广路径
在这里插入图片描述
在这里插入图片描述

算法流程

所以流程就是:
1,对于一个未匹配的节点u,寻找它的每条边,如果它的边上的另一个节点v还没匹配则表明找到了一个匹配,直接转步骤4;
2,假如节点u它边上的另一个节点v已经匹配,那么就转向跟v匹配的节点,假设是w,然后再对w重复1,2的步骤,即寻找增广路.
3,假如我们在1,2步过程中找到一条增广路, 那么修改各自对应的匹配点,转步骤4,若无增广路, 则退出.
4,匹配数+1;
在这里插入图片描述

本题参考代码

#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define in rad()
inline int rad(){
	int x=0,f=1;char c=getchar();while(c>'9'||c<'0')c=getchar();
	if(c=='-')f=-1,c=getchar();while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
	return x*f;
}
const int maxn=3e2+10;
int n,p;
int mp[maxn][maxn],vis[maxn],match[maxn];
int find(int x){
	for(int i=1;i<=n;i++){
		if(mp[x][i] && !vis[i]){
			vis[i]=1;
			if(match[i]==-1 || find(match[i])){
				match[i]=x;
				return 1;
			}
		}
	}
	return 0;
}
int km(){
	int ret=0;
	memset(match,-1,sizeof(match));
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		ret+=find(i);
	}
	return ret;
}
int main(){
	int t=in;
	while(t--){
		memset(mp,0,sizeof(mp));
		p=in;n=in;
		for(int i=1;i<=p;i++){
			int x=in;
			while(x--){
				int y=in;
				mp[i][y]=1;
			}
		}
		int ans=km();
		if(ans==p)puts("YES");else puts("NO");
	}
	
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值