Description
Input
Output
Sample Input
5 2 3 5 0 1 4 5 3 0 1 2 5 0 1 2 3 0 4 3 2 1 0
Sample Output
3 1 3 5 2 2 4
Source
给定 n 个人的认识关系有向图,将他们分为 2 组人,每组的人要相互认识,且两组人数相差尽量小。
构建新图,两个人不相互认识则连一条无向边。然后二分染色(同时记录每个联通块内有哪些点son[i][++num[i]],数量差dis[i]: 0 颜色 - 1 颜色),如果不是二分图,那么就无解。
dp[i][j]表示前i个联通块分两组后第1组-第2组=j的分法是否存在。
差值可能是负数,因此坐标平移,j=差值+100。
dp[i-1][j]==1 则 dp[i][j+dis[i]]=1,dp[i][j-dis[i]]=1;
同时记录新状态是否是由前一状态的差值-dis[i]转移来的:
path[i][j+dis[i]]=1,path[i][j-dis[i]=0;
dp[cnt][ d+100]==1中绝对值最小的 d 就是最小差值,然后从第cnt个联通块推回去,如果path[i][j]^color[当前联通块的点]==1说明这个点是第1组的,因为path[i][j]==1代表第i个联通块的0颜色给第一组。异或为1,则path为0,color为1或者path为1,color为0。
path[i][j]为1,则第 i-1 个联通块时的差值就是当前差值+dis[i]。否则-dis[i]。
#include <cstdio>
#define N 205
#define sf(a) scanf("%d",&a)
using namespace std;
int n,g[N][N],gg[N][N];//原图和新图
int color[N];//染色
int dis[N];//一个联通块里0色-1色的数量差
int cnt,son[N][N];//储存1~cnt联通块的节点
int num[N];//1~cnt联通块的个数
int dp[N][N],path[N][N];//dp和路径记录
int tot[2],team[2][N];//记录两个队的人数和队员
int cl[2];//0色和1色的数量
int dfs(int x,int c){//将x染上c色(0,1)
	color[x]=c;
	for(int i=1;i<=n;i++)if(gg[x][i]&&i!=x){
		if(color[i]==c)return 0;
		if(color[i]==-1&&!dfs(i,!c))return 0;
	}
	cl[c]++;
	son[cnt][++num[cnt]]=x;
	return 1;
}
int check(){
	for(int i=1;i<=n;i++)if(color[i]==-1){
		cnt++;
		cl[1]=cl[0]=0;
		if(!dfs(i,1))return 0;
		dis[cnt]=cl[0]-cl[1];
	}
	return 1;
}
void solve(){
	dp[0][100]=1;
	for(int i=1;i<=cnt;i++)
	for(int j=0;j<=200;j++)if(dp[i-1][j]){
		dp[i][j+dis[i]]=1;
		path[i][j+dis[i]]=1;
		if(j>dis[i]){
			dp[i][j-dis[i]]=1;
			path[i][j-dis[i]]=0;
		}
	}
	int d=0;
	for(;d<100&&!(dp[cnt][d+100]||dp[cnt][-d+100]);d++);
	if(dp[cnt][d+100])d+=100;else d=100-d;
	for(int i=cnt;i;i--){
		for(int j=1;j<=num[i];j++){
			if(path[i][d]^color[j])team[1][++tot[1]]=son[i][j];	
			else team[0][++tot[0]]=son[i][j];
		}
		if(path[i][d]) d-=dis[i];
		else d+=dis[i];
	}
	for(int j=0;j<2;j++)
	{
		printf("%d ",tot[j]);
		for(int i=1;i<=tot[j];i++)
			printf("%d ",team[j][i]);
		puts("");
	}
}
int main() {
	sf(n);
	for(int i=1;i<=n;i++){
		int x;
		while(sf(x),x)
			g[i][x]=1;
		color[i]=-1;
	}
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(!g[i][j]||!g[j][i])
				gg[i][j]=gg[j][i]=1;
	if(check()) solve();
	else puts("No solution");
}
【POJ 1112】Team Them Up!(二分图染色+DP)
原文:http://www.cnblogs.com/flipped/p/5781741.html