hdu 1083

题目概述:

有times组数据

有P个课程,N个人

每个课程可由times(和上面的不是一个变量)个人学,可学的人编号为num

输入:

第一行times(组数据),下一行P,N,其后P行,每行一个times(个人),其后times个num

输入共有times组

限制:

1<=P<=100;1<=N<=300;0<=times(个人)<=N;1<=num<=N;

输出:

每行一个字符串,如果每个课都有学生学,而每个学生至多学一门课,则为

YES

否则为

NO

多组输出之间没有空行

样例输入:

3
3 3
3 1 2 3
2 1 2
1 1
3 3
2 1 3
2 1 3
1 1
1 2
1 1

样例输出:

YES
NO
YES

讨论:

不难看出这就是个二分图匹配,课程-学生

其实这里可以进行一点小小的优化,一个是当课程数量多于学生数量时,由于每个学生至多选一门课,因此必然有课无法被选,另外,在进行匈牙利时,可以不用记录cnt,如果无法为某个课分配任何学生,也可以直接出结果

虽然匈牙利算法的实质是不断找增广路(从未匹配的点出发,依次经过匹配边,未匹配边……直到途径一个未匹配的点,则此路径为增广路),但额更倾向于理解为一个“让”的过程,能让则让,让不了就真的无法匹配了

1.这里牵扯到一个递归,如果该学生尚未选过课,那么当前的课就归这个学生,如果曾经选过课,则递归到那个课,尝试为那个课找一个别的学生,由于marked数组的存在,因此那个课不会再分配到刚才的那个学生,而是从其他学生里找一个替换,因此为每个课分配的学生是在不断更新的而非一成不变

题解状态:

265MS,1744K,1164 B,C++

#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<vector>//这里纯属多余,本打算用邻接表,但由于要判断是否存在路所以改用邻接矩阵
using namespace std;
#define INF 0x3f3f3f3f
#define maxx(a,b) ((a)>(b)?(a):(b))
#define minn(a,b) ((a)<(b)?(a):(b))
#define MAXN 305

int P,N;//实际上P并没有全局的必要,只是为了统一形式而已
bool edges[104][MAXN];//由于课只有100个,开到104足以
bool marked[MAXN];//编号为下标的学生在为某课分配学生时是否已使用过
int edgeto[MAXN];//下标为学生编号,值为课程编号
bool hungary(int a)//find已有该函数,改用匈牙利的英文hungary
{
	for (int p = 1; p <= N; p++) {//遍历所有学生
		if (edges[a][p] && !marked[p]) {//如果该课可被该学生选并且该学生在此轮选课中尚未使用过
			marked[p] = true;//该学生现在已经被使用
			if (!edgeto[p] || hungary(edgeto[p])) {//参见讨论1
				edgeto[p] = a;//为该课程分配(重新分配)学生
				return true;//完成一次选课
			}
		}
	}
	return false;//选课没有完成,该课程无法被任何学生选
}
void fun()//由于N和P都是全局的,所以不需要任何参数
{
	for (int p = 1; p <= P; p++) {
		int times;
		scanf("%d", &times);//input
		while (times--) {
			int num;
			scanf("%d", &num);//input//并不用在意课程号和学生号重复的现象,因为分别对应矩阵的横坐标和纵坐标,二者也不存在会混淆的地方
			edges[p][num] = true;
		}
	}//input ends here
	int cnt = 0;//记录成功找到匹配的个数
	for (int p = 1; p <= P; p++) {
		memset(marked, 0, sizeof(marked));//所谓选课与否是相对于为当前课程分配学生时,因此每次需要清空
		if (hungary(p))
			cnt++;//每次匈牙利成功找到匹配便加1
	}
	if (cnt == P)//所有课程都有对应学生
		printf("YES\n");//output
	else
		printf("NO\n");//output
}
int main(void)
{
	//freopen("vs_cin.txt", "r", stdin);
	//freopen("vs_cout.txt", "w", stdout);

	int times;
	scanf("%d", &times);
	while (times--) {
		scanf("%d%d", &P, &N);//input
		fun();
		memset(edgeto, 0, sizeof(edgeto));//有必要清空,谁也不希望被前人分配的结果混淆
		memset(edges, 0, sizeof(edges));//有必要清空,修改课的可分配情况时并没有对所有情况进行更新
	}
}

EOF

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值