UVA1627 团队分组

题目大意:有n个人,把他们分成非空的两组,使得每个人都被分到一组,要求组内成员相互认识,并且两组人数差距最小。多解输出任意方案,无解输出No solution.

根据题意,如果有两个人不是互相认识,那么这两个人只能被分到不同的组。于是想到在不是相互认识的人之间连上一条边(注意一仅仅是一个人认识另一个人也需要连边),这样我们就得到了若干个连通分量。对于每个连通分量,相邻的两个点是必须分到两个组的,由此我们可以想到二分图染色。从连通分量任意一点出发,依次把点染色为0,1……对于一个点如果相邻点已经染色并且颜色和自己一样,则说明无解。对于每个连通分量,我们把它包含的点按照染上的颜色分为两组a,b,存到一个数组。

假如我们已经判断出了有tot个连通分量,并且设有两组A,B,那么对于每个连通分量都有两个决策:把a分到A组或者把a分到B组。有决策,有状态,我们很容易发现可以用动态规划求解,并且模型类似于01背包问题。

设f[i][j]表示讨论到第i个连通分量并且当前A组有j个人的状态是否能达到。转移方程如下:

f[i][j]=f[i-1][j-cnta](表示把a分到A组)

f[i][j]=f[i-1][j-cntb](表示把a分到B组)

之后就是使用相应的数据结构输出相应的解。

注意:下面贴出的代码并没有在virtual judge的uva 1627上AC。但是我认为没有写错……但是没有找到比较简单的数据反例。仅供参考。希望大神指正。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<stack>
using namespace std;
bool toget[105][105],havans=1,haveprint[105];
int t,n,tot,col[105];
struct Edge{
	int from,to;
	Edge(int from,int to):from(from),to(to){}
};
struct Divis{
	int lcnt,rcnt;
	vector<int>lob,rob;
	void init(){
		lcnt=rcnt=0;
		lob.clear();
		rob.clear();
	}
}obj[105];
struct Situa{int val,ps;}f[105][105];
vector<Edge>edges;
vector<int>G[105];
void addedges(int from,int to){
	edges.push_back(Edge(from,to));
	G[from].push_back(edges.size()-1);
}
void clear(){
	edges.clear();
	for(int i=1;i<105;i++)G[i].clear();
	memset(toget,0,sizeof(toget));
	memset(col,-1,sizeof(col));
	for(int i=1;i<105;i++)obj[i].init();
	memset(f,0,sizeof(f));
	tot=0;
	havans=1;
	memset(haveprint,0,sizeof(haveprint));
}
void dfs(int u,int d){
	if(d){
		obj[tot].lcnt++;	
		obj[tot].lob.push_back(u);
	}
	else {
		obj[tot].rcnt++;
		obj[tot].rob.push_back(u);
	}
	col[u]=d;
	for(int i=0;i<G[u].size();i++){
		Edge e=edges[G[u][i]];
		if(col[e.to]>=0){
			if(d==col[e.to])havans=0;
			continue;
		}
		dfs(e.to,d^1);
	}
}
int main(){
	int v;
	cin>>t;
	while(t--){
		clear();
		cin>>n;
		for(int i=1;i<=n;i++)
			while(cin>>v&&v)toget[i][v]=1;
		for(int i=1;i<n;i++)
			for(int j=i+1;j<=n;j++)
				if(toget[i][j]==0||toget[j][i]==0){
					addedges(i,j);
					addedges(j,i);
				}
		for(int i=1;i<=n;i++)
			if(col[i]==-1){
				tot++;
				dfs(i,1);
			}
		if(!havans){
			puts("No solution");
			continue;
		}
		f[0][0].val=1;
		for(int i=1;i<=tot;i++){
			for(int j=obj[i].lcnt;j<=n;j++)
				if(f[i-1][j-obj[i].lcnt].val){
					f[i][j].val=1;
					f[i][j].ps=0;
				}
			for(int j=obj[i].rcnt;j<=n;j++)
				if(f[i-1][j-obj[i].rcnt].val){
					f[i][j].val=1;
					f[i][j].ps=1;
				}
		}
		int cur1=tot,cur2=0,ans=n,t2=0;
		for(int i=min(obj[tot].lcnt,obj[tot].rcnt);i<=n;i++)
			if(f[tot][i].val&&abs(2*i-n)<ans)ans=abs(2*i-n),cur2=i;	
		t2=n-cur2;
		if((!t2)||(!cur2)){
			puts("No solution");
			continue;
		}	
		printf("%d ",cur2);
		stack<int>ansnotes;
		while(true){
			int temp;
			ansnotes.push(f[cur1][cur2].ps);
			if(cur1==1)break;
			if(!f[cur1][cur2].ps)temp=obj[cur1].lcnt;
			else temp=obj[cur1].rcnt;
			cur1--,cur2-=temp;
		}
		while(!ansnotes.empty()){
			int temp=ansnotes.top();
			ansnotes.pop();
			if(!temp)
				for(int i=0;i<obj[cur1].lcnt;i++){
					haveprint[obj[cur1].lob[i]]=1;
					printf("%d ",obj[cur1].lob[i]);
				}
			else {
				for(int i=0;i<obj[cur1].rcnt;i++){
					haveprint[obj[cur1].rob[i]]=1;
					printf("%d ",obj[cur1].rob[i]);
				}
			}
			cur1++;
		}
		printf("\n%d ",t2);
		for(int i=1;i<=n;i++)
			if(!haveprint[i])printf("%d ",i);
		putchar(10);
	}
}
/*
2
5
3 4 5 0
1 3 5 0
2 1 4 5 0
2 3 5 0
1 2 3 4 0
5
2 3 5 0
1 4 5 3 0
1 2 5 0
1 2 3 0
4 3 2 1 0
*/

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值