Girls and Boys
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8526 Accepted Submission(s): 3909
The input contains several data sets in text format. Each data set represents one set of subjects of the study, with the following description:
the number of students
the description of each student, in the following format
student_identifier:(number_of_romantic_relations) student_identifier1 student_identifier2 student_identifier3 ...
or
student_identifier:(0)
The student_identifier is an integer number between 0 and n-1, for n subjects.
For each given data set, the program should write to standard output a line containing the result.
7 0: (3) 4 5 6 1: (2) 4 6 2: (0) 3: (0) 4: (2) 0 1 5: (1) 0 6: (2) 0 1 3 0: (2) 1 2 1: (1) 0 2: (1) 0
5 2
题目大意:
大二的时候,有些人开始研究男女间的恋爱关系。
研究者想要找出没有恋爱关系的同学的最大集合,
也就是在此集合中,任意两个同学都不存在恋爱关系。
得到结果就是这个集合中学生的数量。
解题思路:
求二分图的最大独立集。
可以邻接表或邻接矩阵构图,但邻接矩阵内存开销大。
因为题目只给出学生的个数,未明确给出第i个学生是男生还是女生,所以要遍历所有学生。
因此相同的增广路求了两遍,最后得到的最大匹配count要除以2。
例如测试样例一:
7
0: (3) 4 5 6
1: (2) 4 6
2: (0)
3: (0)
4: (2) 0 1
5: (1) 0
6: (2) 0 1
用匈牙利算法得最大匹配的边为:
<0,5>,<5,0>,<1,4>,<4,1>
题目要求:0和5,1和4不能属于同一顶点集(同一性别),
则存在对称边,也就是相同的增广路求了两遍。
所以得到的最大匹配要除以2。
最后得到的最大独立集为 A={2,3,4,5,6} (假设0,1,2,3为男生,4,5,6为女生就好理解了)
答案为5。
/*邻接矩阵(内存开销大)*/
#include <cstdio>
#include <cstring>
const int maxn = 1010;
bool rom[maxn][maxn],vis[maxn];
int stu[maxn];
int n;
void init()
{
int i,j,nu,s,e;
memset(rom,0,sizeof(rom));
memset(stu,-1,sizeof(stu)); //因为存在0元素,所以stu[]初始为-1
for(i=0;i<n;++i)
{
scanf("%d: (%d)",&s,&nu); //注意输入
for(j=0;j<nu;++j)
{
scanf("%d",&e);
rom[s][e]=true;
}
}
}
bool find(int p)
{
int i;
for(i=0;i<n;++i)
{
if(rom[p][i]==true&&!vis[i])
{
vis[i]=true;
if(stu[i]==-1||find(stu[i]))
{
stu[i]=p;
return true;
}
}
}
return false;
}
int main()
{
int i,count;
while(scanf("%d",&n)==1)
{
init();
count = 0;
for(i=0;i<n;++i)
{
memset(vis,0,sizeof(vis));
if(find(i))
count++;
}
printf("-%d-%d-",count,count/2);
printf("%d\n",n-count/2);
}
return 0;
}
/*邻接表*/
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 1010;
vector<int > rom[maxn]; //用vector建邻接表
bool vis[maxn];
int stu[maxn];
int n;
void init()
{
int i,j,k,nr,s,e;
for(i=0;i<=n;++i)
{
rom[i].clear();
stu[i]=0;
}
for(i=0;i<n;++i)
{
scanf("%d: (%d)",&s,&nr);
rom[s+1].push_back(0); //每个数组的第一个值赋为0,大小+1
for(j=0;j<nr;++j)
{
scanf("%d",&e);
rom[s+1].push_back(e+1); //令下标从1开始
}
}
}
bool find(int p)
{
int i,k;
for(i=1;i<rom[p].size();++i) //注意下标从1开始(rom[p][0]==0),且不能等于(因为数组的下标从0开始)
{
k=rom[p][i];
if(!vis[k]) //因为是邻接表,所以不需要如邻接矩阵一样判断rom[p][i]
{
vis[k]=true;
if(stu[k]==0||find(stu[k]))
{
stu[k]=p;
return true;
}
}
}
return false;
}
int main()
{
int i,count;
while(scanf("%d",&n)==1)
{
init();
count=0;
for(i=1;i<=n;++i) //注意下标从1开始
{
memset(vis,0,sizeof(vis));
if(find(i))
count++;
}
printf("%d\n",n-count/2); //因为关系是对称的,相同增光路遍历了两遍,所以最大匹配为count/2
}
return 0;
}