POJ 1112 Team Them Up! 二分图判定+01背包
题目链接:
http://poj.org/problem?id=1112
Team Them Up!
Memory Limit: 10000K
样例输出
3 1 3 5
2 2 4
题意
给你若干个人,要把他们分成两组,其中同一组内要保证任意两个人互相认识(题目给的边都是单向边),求一个使得两组人数最接近的方案,要求输出一个可行分组方案。
题解
首先,把关系图建处理(对于(u,v)如果不存在(v,u)相当于这条边不存在),处理出补图,然后题目就转换成了一个二分图问题了,跑下黑白染色,判断是否有可行解,如果有,则用dp跑下,求出最优解就ok啦。dp[i][j]表示已经处理了i个联通分量(跑黑白染色会处理出若干个联通分量),其中一组能凑出j个人的方案。
代码
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<sstream>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;
const int INF=0x3f3f3f3f;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-8;
const double PI = acos(-1.0);
//start----------------------------------------------------------------------
const int maxn=111;
int n;
bool gra[maxn][maxn];
bool dp[maxn][maxn];
VI lis[maxn][3];
int color[maxn],tot;
//黑白染色
bool dfs(int u) {
for(int v=1; v<=n; v++) {
if(!gra[u][v]) continue;
if(!color[v]) {
color[v]=3-color[u];
lis[tot][color[v]].pb(v);
bool su=dfs(v);
if(!su) return false;
} else {
if(color[v]==color[u]) return false;
}
}
return true;
}
VI ans[3];
bool used[maxn];
//处理出最优方案
void print(int i,int j) {
if(i==0) {
return;
}
int a=lis[i][1].sz(),b=lis[i][2].sz();
if(j>=a&&dp[i-1][j-a]) {
print(i-1,j-a);
rep(ii,0,lis[i][1].sz()) {
ans[1].pb(lis[i][1][ii]);
}
} else if(j>=b&&dp[i-1][j-b]) {
print(i-1,j-b);
rep(ii,0,lis[i][2].sz()) {
ans[1].pb(lis[i][2][ii]);
}
}
}
int main() {
while(scf("%d",&n)==1&&n) {
clr(gra,0);
for(int i=1; i<=n; i++) {
int v;
while(scf("%d",&v)==1&&v) {
gra[i][v]=1;
}
}
for(int i=1; i<=n; i++) {
for(int j=1; j<i; j++) {
gra[i][j]=gra[j][i]=!(gra[i][j]&&gra[j][i]);
}
}
//黑白染色
clr(color,0);
rep(i,0,maxn) lis[i][1].clear(),lis[i][2].clear();
tot=0;
bool su=1;
for(int i=1; i<=n; i++) {
if(!color[i]) {
++tot;
color[i]=1;
lis[tot][1].pb(i);
su=dfs(i);
}
if(!su) break;
}
if(!su) {
prf("No solution\n");
continue;
}
//01背包,不是选和不选,而是选1还是选2.
clr(dp,0);
dp[0][0]=1;
for(int i=1; i<=tot; i++) {
int a=lis[i][1].sz(),b=lis[i][2].sz();
for(int j=0; j<=100; j++) {
if(j>=a&&j>=b) {
dp[i][j]=dp[i-1][j-a]|dp[i-1][j-b];
} else if(j>=a) {
dp[i][j]=dp[i-1][j-a];
} else if(j>=b) {
dp[i][j]=dp[i-1][j-b];
}
}
}
//输出最优方案
int Mi=INF,pos=-1;
for(int i=1; i<=100; i++) {
if(dp[tot][i]) {
if(Mi>abs(i-(n-i))) {
Mi=abs(i-(n-i));
pos=i;
}
}
}
ans[1].clear(),ans[2].clear();
clr(used,0);
print(tot,pos);
rep(i,0,ans[1].sz()) used[ans[1][i]]=1;
for(int i=1; i<=n; i++) {
if(!used[i]) ans[2].pb(i);
}
sort(all(ans[1]));
sort(all(ans[2]));
for(int i=1; i<=2; i++) {
prf("%d ",ans[i].sz());
rep(j,0,ans[i].sz()) prf("%d%c",ans[i][j],j==ans[i].sz()-1?'\n':' ');
}
}
return 0;
}
//end-----------------------------------------------------------------------
/*
3
2 0
1 3 0
2 0
*/