Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 10733 | Accepted: 4204 |
Description
Input
Output
Sample Input
2 3 3 3 1 2 3 2 1 2 1 1 3 3 2 1 3 2 1 3 1 1
Sample Output
YES NO
题目大意就是n个学生去p个课堂,每一个学生都有自己的课堂,并且每个学生只能去一个课堂,题目要求能够安排每一个课堂都有人吗?
输入数据的第一行是测试数据的个数,每组测试数据的开始分别是p和n,接着p行,每行的开始是这个课堂的学生人数m,接着m个数代表该课堂的学生编号,对于输出,如果该组数据能够这样安排就输出YES,否则输出NO。
例如,对于第一组数据明显可以这样匹配,3-3,2-2,1-1,而对于第二组数据则无法找到匹配方案,
这题明显的求二分图的最大匹配,关于该算法详见POJ 1274 The Perfect Stall 解题报告
但做这题的时候,用临界矩阵做刚开始时数组开小了,RE了一次,第二次TLE,后改为临界表,依旧TLE,最后,无奈,把cin全换成scanf时过了,在此要感谢laputa大神的提醒,Orz!
果然就过了!
参考代码:
1 #include<iostream>
2 #include<cstdlib>
3 #include<cstdio>
4 #include<cstring>
5 #include<algorithm>
6 #include<cmath>
7 using namespace std;
8 const int maxn = 301*101;
9 int first[maxn],next[maxn],v[maxn];//头结点,下一点,和边的终点(即学生编号)
10 bool vis[maxn]; //记录是否访问过
11 int link [maxn];//记录与之匹配的点
12 int p , n ;
13 bool find ( int k )
14 {
15 int i ;
16 for ( i = first[k] ; i != -1 ; i =next [i] )
17 {//寻找与k连接的点
18 if ( !vis [v[i]] )
19 {//如果没访问过,则访问并标记
20 vis [ v[i]] =true ;
21 if ( link [v[i]] == 0 || find ( link [v[i]] ) )
22 {//如果该学生还未匹配课堂或存在增广路
23 link [v[i]] = k ;//与之匹配
24 return true ;
25 }
26 }
27 }
28 return false;
29 }
30 int main()
31 {
32 int t ;
33 scanf("%d",&t) ;
34 while ( t -- )
35 {
36 scanf("%d%d",&p,&n) ;
37 if ( p > n ) //如果学生人数都少于课堂数,明显不可能达到匹配
38 cout <<"NO"<<endl;
39 else
40 {
41 int i , j ;
42 memset ( first , -1 , sizeof ( first ) ); //初始化表头
43 memset ( link , 0 , sizeof ( link ) ) ; //初始化
44 int e = 0 ;
45
46 for ( i = 0 ; i < p ; i ++ )
47 {
48 int a ;
49 scanf("%d",&a);
50 for ( j = 0 ; j < a ; j ++ , e ++ )
51 {
52 scanf("%d",&v[e]);
53 next [e] = first [i+1]; //插入链表,从头端插入
54 first [i+1] = e ; //记录链表头结点
55 }
56 }
57 bool ans = 0 ;
58 for ( i = 0 ; i < p ; i ++ )
59 {
60 memset ( vis , 0 , sizeof ( vis ) ) ;
61 if ( !find ( i + 1 ) )
62 {//如果找不到与改点匹配的点,则答案就为NO
63 ans =true;
64 break;
65 }
66 }
67 if ( ans )
68 cout<<"NO"<<endl;
69 else
70 cout<<"YES"<<endl;
71 }
72 }
73 return 0;
74 }