hdu 1083

来源:互联网 发布:手机淘宝怎么用集分宝 编辑:程序博客网 时间:2024/06/07 11:38

题目概述:

有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 305int 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])) {//参见讨论1edgeto[p] = a;//为该课程分配(重新分配)学生return true;//完成一次选课}}}return false;//选课没有完成,该课程无法被任何学生选}void fun()//由于N和P都是全局的,所以不需要任何参数{for (int p = 1; p <= P; p++) {int times;scanf("%d", &times);//inputwhile (times--) {int num;scanf("%d", &num);//input//并不用在意课程号和学生号重复的现象,因为分别对应矩阵的横坐标和纵坐标,二者也不存在会混淆的地方edges[p][num] = true;}}//input ends hereint 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");//outputelseprintf("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);//inputfun();memset(edgeto, 0, sizeof(edgeto));//有必要清空,谁也不希望被前人分配的结果混淆memset(edges, 0, sizeof(edges));//有必要清空,修改课的可分配情况时并没有对所有情况进行更新}}

EOF

0 0
原创粉丝点击