Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 24326 | Accepted: 9484 |
Description
- every student in the committee represents a different course (a student can represent a course if he/she visits that course)
- each course has a representative in the committee
Input
P N
Count1 Student 1 1 Student 1 2 ... Student 1 Count1
Count2 Student 2 1 Student 2 2 ... Student 2 Count2
...
CountP Student P 1 Student P 2 ... Student P CountP
The first line in each data set contains two positive integers separated by one blank: P (1 <= P <= 100) - the number of courses and N (1 <= N <= 300) - the number of students. The next P lines describe in sequence of the courses �from course 1 to course P, each line describing a course. The description of course i is a line that starts with an integer Count i (0 <= Count i <= N) representing the number of students visiting course i. Next, after a blank, you抣l find the Count i students, visiting the course, each two consecutive separated by one blank. Students are numbered with the positive integers from 1 to N.
There are no blank lines between consecutive sets of data. Input data are correct.
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
Source
解题思路:直接求这个二分图的最大匹配数是否等于p就行。
最近刚学匈牙利算法,下面说一下我对这个算法的理解,以及一些二分图的一些简单性质。
首先说一些什么叫做二分图,那些繁琐的定义就不需要说了,简单一点,就一句话,如果图G的所有边的两个端点可以分为两个不相交的集合,这个图就是二分图。
那么你肯定想知道, 给你任意一个图,怎样判断这个图是不是二分图。
这个问题也很简单,一个图是二分图的充要条件是这个图中没有大于等于3的奇圈(环的长度),这个用代码判断也很简单,用并查集搞一下就行。
下面说一下匹配的概念,所谓匹配,说白了就是一个边集,这个边集中没有两条边有公共的端点,这个边集就就可以叫做一个匹配。边集里面边的数量就做这个匹配的匹配数。 而二分图的最大匹配数,就是让你找一个匹配,使这个匹配的匹配数最大。
下面介绍另外一些跟二分图有关的概念。
最大独立集(点):在N个点的图G中选出m个点,使这m个点两两之间没有边,求m最大值。
很显然最大独立集的大小为 n - 最大匹配数(如果这个图是一个二分图,n为这个图顶点的数量)
最小路径覆盖:用尽量少的不相交简单路径覆盖有向无环图G的所有结点。解决此类问题可以建立一个二分图模型。把所有顶点i拆成两个:X结点集中的i和Y结点集中的i',如果有边i->j,则在二分图中引入边i->j',设二分图最大匹配为m,则结果就是n-m。
最小点覆盖:找出数量最少的点,使得所有的边的至少一个端点在这个集合里,显然最小点覆盖的大小为最大匹配数
从上面的一些概念中我们可以知道,很多问题可以转化为求二分图的最大匹配问题。那么求一个二分图的最大匹配数就显得尤为重要了。
而匈牙利算法就是求解二分图最大匹配的一个算法。在介绍这个算法之前,先讲一下什么叫做增广路径。
一条增广路径是相对于一个匹配来说的,假设现在有一个匹配,如果一条路径满足:
1.这条路径上的第一个点和最后一个点都是未匹配点(这个点相连的边不是匹配中的边)。
2.匹配边和未匹配边交替出现,且第一条边和最后一条边为为匹配边。
那么我们称这条路径为这个匹配的一条增广路径,很显然,增广路径一定有奇数条边,并且匹配边的数量为偶数,未匹配边的数量为奇数,未匹配边数量刚好比匹配边数量多1.
由上面的增广路径定义可知,如果我们能够找到一个匹配的一条增广路径,那么我们可以用这条增广路径中的为未匹配边代替匹配边(也就是把这条增广路径中的匹配边删掉),把这些未匹配边当成一个新的匹配,那么当前匹配数量就相对于前面的匹配数量增加了1,所以匈牙利算法求解最大匹配的原理,就是一直找当前匹配的增广路径,直到找不到增广路径为止,这时的匹配就是最大匹配了。
下面是匈牙利算法的代码实现(基于dfs):
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 105;
int p, n;
int ans;
bool visit[3 * maxn];
int Match[3 * maxn];
vector<int> g[maxn];
void init()
{
ans = 0;
for(int i = 1; i <= p; i++)
{
g[i].clear();
}
memset(Match, -1, sizeof(Match));
}
bool dfs(int u)
{
for(int i = 0; i < g[u].size(); i++)
{
int v = g[u][i];
if(visit[v]) continue;
if(Match[v] == -1)
{
Match[v] = u;
return true;
}
visit[v] = true;
if(dfs(Match[v]))
{
Match[v] = u;
return true;
}
}
return false;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &p, &n);
init();
for(int i = 1; i <= p; i++)
{
int num;
scanf("%d", &num);
for(int j = 1; j <= num; j++)
{
int v;
scanf("%d", &v);
g[i].push_back(v);
}
}
for(int i = 1; i <= p; i++)
{
memset(visit, false, sizeof(visit));
if(dfs(i)) ans++;
}
if(ans == p) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}