POJ1112 Team Them Up!

Team them up!

Input fileteams.in
Output fileteams.out

Your task is to divide a number of persons into two teams, in such a way, that:

  • everyone belongs to one of the teams;
  • every team has at least one member;
  • every person in the team knows every other person in his team;
  • teams are as close in their sizes as possible.

This task may have many solutions. You are to find and output any solution, or to report that solution does not exist.

Input

For simplicity, all persons are assigned a unique integer identifier from 1 to N.

The first line in the input file contains a single integer number N (2 ≤ N ≤ 100) - the total number of persons to divide into teams, followed by N lines - one line per person in ascending order of their identifiers. Each line contains the list of distinct numbers Aij (1 ≤ Aij ≤ N, Aij ≠ i), separated by spaces. The list represents identifiers of persons that ith person knows. The list is terminated by 0.

Output

If the solution to the problem does not exist, then write a single message "No solution" (without quotes) to the output file. Otherwise write a solution on two lines. On the first line of the output file write the number of persons in the first team, followed by the identifiers of persons in the first team, placing one space before each identifier. On the second line describe the second team in the same way. You may write teams and identifiers of persons in the team in any order.

Sample input #1

5
3 4 5 0
1 3 5 0
2 1 4 5 0
2 3 5 0
1 2 3 4 0

Output for the sample input #1

No solution

Sample input #2

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 for the sample input #2

3 1 3 5
2 2 4
简单地说,就是,一个N个节点的有向图,将节点分成两个集合,满足以下四个条件: 1. 每个节点属于其中一个集合 2. 每个集合至少有一个节点 3. 集合里的每一个节点都有边连向同一个集合里的其他点 4. 被分成的两个集合的大小要尽量接近

如果不能满足上述条件,输出 No solution ,否则输出这两个集合

题解

参照ylfdrib的题解。

首先,求原图的补图,同时把有向图转化为无向图,即:对于节点u, v,如果没有边<u, v>或<v, u>,则建无向边(u, v)

分析一下现在这个图,如果u, v相连,表明u, v不能在同一个集合里,对于这个问题我们就有了感觉,先求图的连通分量,对于同一个连通分量,我们用二分图着色法,把整个连通分量里的点分到两个集合里,当然,如果着色失败,则无解,输出 No solution ,否则,这m个连通分量就构成了m个集合对(xi, yi),xi表示第i个连通分量中着色为0的集合,yi表示第i个连通分量中着色为1的集合,这样问题就转化成:对这m个集合对,每对里选出一个集合,构成A集合,剩余的构成B集合,要求A,B大小尽量接近,这样我们就可以用动态规划来搞定了。

DP方程:dp[i][j] = (dp[i][j - cnt[i][0]] | dp[i][j - cnt[i][1]] ) (1 <= i <= scc, 0 <= j <= N / 2)

dp[i][j] = 1 表示前i个连通分支,可以构成符合要求的节点数为j的集合

这里暂且用scc表示连通分支个数,N表示总节点个数,cnt[i][0]表示第i个分支中被着色成0的节点个数,cnt[i][1]表示第i个分支中被着色成1的节点个数,同时记录dp路径,这样这道题就算彻底搞定了

co int N=201;
int n,a[N],c[N],num[2],cnt,f[N][N],g[N][N];
pair<int,int> p[N];
#define x first
#define y second
vector<int> e[N],d[N][2],ans[2];

bool dfs(int x,int color){
	c[x]=color,++num[color],d[cnt][color].push_back(x);
	for(unsigned i=0;i<e[x].size();++i){
		int y=e[x][i];
		if(c[y]==-1){
			if(!dfs(y,color^1)) return 0;
		}
		else if(c[y]==color) return 0;
	}
	return 1;
}
void print(int k,int x){
	if(!k) return;
	int t=g[k][x];
	for(int i=0;i<2;++i)
		for(unsigned j=0;j<d[k][i].size();++j)
			ans[i^t].push_back(d[k][i][j]);
	print(k-1,x+d[k][t].size()-d[k][t^1].size());
}
int main(){
	read(n);
	memset(c,-1,sizeof c);
	for(int i=1;i<=n;++i){
		memset(a,0,sizeof a);
		for(int x;read(x);) a[x]=1;
		for(int j=1;j<=n;++j)if(j!=i&&!a[j])
			e[i].push_back(j),e[j].push_back(i);
	}
	for(int i=1;i<=n;++i)if(c[i]==-1){
		num[0]=num[1]=0,++cnt;
		if(!dfs(i,0)) return puts("No solution"),0;
		p[cnt]=make_pair(num[0],num[1]);
	}
	f[0][100]=1;
	for(int i=1;i<=cnt;++i)
		for(int j=0;j<=200;++j)if(f[i-1][j]){
			int x=p[i].x,y=p[i].y;
			f[i][j+x-y]=1,g[i][j+x-y]=1;
			f[i][j+y-x]=1,g[i][j+y-x]=0;
		}
	for(int i=0;i<=100;++i)
		if(f[cnt][100-i]) {print(cnt,i+100);break;}
	printf("%llu ",ans[0].size());
	for(unsigned i=0;i<ans[0].size();++i) printf("%d ",ans[0][i]);
	printf("
%llu ",ans[1].size());
	for(unsigned i=0;i<ans[1].size();++i) printf("%d ",ans[1][i]);
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/11049691.html