您的位置:首页 > 其它

POJ 3692 Kindergarten (补图是二分图的最大团问题)

2013-08-03 15:13 447 查看

题意

幼稚园里有m个男孩和n个女孩(m、n范围都是[1,200]),男孩之间相互认识,女孩之间也相互认识,另外有部分男孩和女孩也认识。现在要举办一个活动,选取一些同学,要求所有选取的同学之间两两相互认识。

思路

抽象一下问题就是:在图G中选择一个最大的子图G‘(V', E'),使得∑u, v∈V’,  (u, v)∈E‘,这就是最大团问题,也即最大完全子图问题。

一般性的最大团问题是NP问题,但是此题的图比较特殊,有g个点互相相连,b个点也互相相连,也就是说他们的补图是一个二分图。那么我们就可以转化为补图G的最大独立子集:∑u, v∈[b]V,  (u, v)∈[/b]E

代码

[cpp]
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, m) for (int i = begin; i < begin+m; i ++)
using namespace std;
const int MAXV = 405; //N1+N2
vector <int> adj[MAXV];
struct MaximumMatchingOfBipartiteGraph{
int vn;
void init(int n){ //二分图两点集点的个数
vn = n;
for (int i = 0; i <= vn; i ++) adj[i].clear();
}
void add_uedge(int u, int v){
adj[u].push_back(v);
adj[v].push_back(u);
}
bool vis[MAXV];
int mat[MAXV]; //记录已匹配点的对应点
bool cross_path(int u){
for (int i = 0; i < (int)adj[u].size(); i ++){
int v = adj[u][i];
if (!vis[v]){
vis[v] = true;
if (mat[v] == 0 || cross_path(mat[v])){
mat[v] = u;
mat[u] = v;
return true;
}
}
}
return false;
}
int hungary(){
MEM(mat, 0);
int match_num = 0;
for (int i = 1; i <= vn; i ++){
MEM(vis, 0);
if (!mat[i] && cross_path(i)){
match_num ++;
}
}
return match_num;
}
void print_edge(){
for (int i = 1; i <= vn; i ++){
for (int j = 0; j < (int)adj[i].size(); j ++){
printf("u = %d v = %d\n", i, adj[i][j]);
}
}
}
}match;

bool mat[MAXV>>1][MAXV>>1];
int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int g, b, m;
int ca = 1;
while(scanf("%d %d %d", &g, &b, &m), g+b+m){
match.init(g+b);
REP(i, 1, g)
REP(j, 1, b)
mat[i][j] = true;
REP(i, 0, m){
int x, y;
scanf("%d %d", &x, &y);
mat[x][y] = false;
}
REP(i, 1, g)
REP(j, 1, b)
if (mat[i][j])
match.add_uedge(i, j+g);
printf("Case %d: %d\n", ca++, g+b-match.hungary());
}
return 0;
}
[/cpp]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: