Kindergarten POJ - 3692
可以不看定义直接往下拉
最大团的定义:
设一个无向图G(V,E) ,V 为点集,E 为两点间的边集。设U 为V 的一个子集,若对于任意的结点对u ,v 属于U 都有边连通,则称点集U构成的图为 完全子图 。无向图G 的完全子图U 是G 的团,G 的最大团即为G 的最大完全子图。
最大团与最大独立集的关系:
求解一个图中的最大独立集等价于求解其补图的最大团。
独立集的条件是任意两个点互不连通,那么如果把原图中连通的点之间的边删除,不连通点连接,即转化为求个数最多的两两连通点集,也即求最大团。
二分图的最大独立集
二分图的最大独立集=点数-最小顶点覆盖
证明:
最小顶点覆盖是图G中任意边都有至少一个端点属于S的顶点集合(S为顶点覆盖集合),那么将这些被覆盖的边删完,剩下的不就是没有关系即没有连通了点了吗
我们可以这样想,先把所有的点放进集合,然后删去最少的点和与之相关联的边,使得全部边都被删完,这就是最小点覆盖。所以有:最大独立集=点数-最小点覆盖
/*---------------------------------------------------------------------------------------------------------*/
题意:一群男孩子和女孩子,男孩子之间相互了解,女孩子之间也相互了解,有一些男孩子和女孩子之间也相互了解,现在要搞一个活动,要你选出一群人,这个人之间都要相互了解,问你这群人的最大数目是多少。
由题意知:这男生和女生两个集合是两个完全图:顶点两两之间有且只有一条边连接,即每对不同的顶点之间都恰连有一条边相连
那么我们现在稍微整理一下上面的定义(不用回去看)
最大完全数:最大完全子图中顶点的个数
独立集:图中任意两点都不相连的顶点集合
完全子图:任意两点都相连的顶点集合
本题求的是两两之间存在联系的最大集合,即图中最大完全子图的顶点个数。二分图中的左右集合是没有内部边的,但是本题的图不符合二分图的定义,所以我们将之转化为二分图求解,即将原本的图转过来,将原本有联系的两个点拆开,将原本没有联系的连接,那么求此新图的最大独立集,独立集是两两互无关系,那么推回原图就是两两互有联系了。。。十分巧妙的一个题目
用更严格一点的说法就是两个完全子图组成原图,其补图一定是二分图。二分图的最大独立集即为所求,因为独立集两两之间没有边,代表原图两两彼此有关系。----- POJ 3692 Kindergarten 题解 《挑战程序设计竞赛》-码农场
这是我之前不理解的地方,为什么说其补图一定是二分图,你想,两个完全子图内部两两恰有一边相连,两图之间用任意边连接(可以暂时忽略)组成一张新图,其补图就是原本相连的点,拆开,原本不相连的点连接,两个子图A,B即两个点集内部两两互不连接,符合二分图的性质,易证补图的每条边所关联的两个顶点分别属于点集A,B
#include<iostream>
#include<cstring>
#include<math.h>
#include<stdlib.h>
#include<cstring>
#include<cstdio>
#include<utility>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
int girl[maxn],head[maxn],vis[maxn][maxn],used[maxn];
int n,m,s,t,k,g,b;
const int inf=0x3f3f3f3f;
int maxflow=0;
int cnt;//这个地方要注意一下反向边的异或,如果从下标1开始,那么他的反向边是0
struct node{
int to,next;
}star[500100];
inline int read(){
int X=0,w=0;char ch=0;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
/*----------------------------------------------------------------------*/
void add(int u,int v){
star[cnt].to=v;
star[cnt].next=head[u];
head[u]=cnt++;
}
int Find(int idx){
int now;
for(int i=head[idx];i!=-1;i=star[i].next){
now=star[i].to;//若两人之间有关系且在这次查找中没有被询问
if(!used[now]){
used[now]=1;//标志该男生在此次查找中已被询问
if(!girl[now]||Find(girl[now])){//腾位置,撬墙脚
girl[now]=idx;//符合条件则将该女生和该男生匹配上,并标记
return 1;
}
}
}
return 0;
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
//最大独立团问题
int step=1;
while(cin>>g>>b>>k){
if(g==0&&b==0&&k==0)break;
cnt=0;
memset(head,-1,sizeof(head));
memset(girl,0,sizeof(girl));
memset(vis,0,sizeof(vis));
int x,y;
for(int i=1;i<=k;++i){
cin>>x>>y;
vis[x][y]=1;
//add(x,y);
}
for(int i=1;i<=g;++i){
for(int j=1;j<=b;++j)
if(!vis[i][j])add(i,j);
}
//x=read();
ll ans=0;
for(int i=1;i<=g;++i){
memset(used,0,sizeof(used));
if(Find(i))ans++;
}
cout<<"Case "<<step++<<": "<<g+b-ans<<endl;
}
return 0;
}
附:
https://blog.csdn.net/qq_41730082/article/details/81456611 图论——最大团问题和最大独立集、二分图相关_上总介的博客-CSDN博客_最大团和最大独立集