小Z的游戏分队

题目描述
小Z受不了寂寞,准备举办一次DOTA比赛,为了能让ACM班全部都参加比赛,他还特制了一张DOTA地图能够支持任意多人打任意多人。

现在问题来了,怎么把这么多人分成两队?小Z的想法是,每个人报上自己愿意同队的同学,接着小Z会按如下要求将所有人分为两队:

对任意同学甲,和同学甲同队的人,必须都是同学甲愿意同队的同学。

小Z希望两队的人数差尽量小,如果这种分组不存在,那么输出No solution。

输入格式
第1行为N,表示一共有多少个学生。

之后2~N+1行,每行表示这个学生信任的同学的名单,以0结束。

输出格式
1行,如果解存在,输出两队的人数,将人数比较小的那队放在前面;如果解不存在,输出No solution。

输入输出样例
输入 #1复制
5
3 4 5 0
1 3 5 0
2 1 4 5 0
2 3 5 0
1 2 3 4 0
输出 #1复制
No solution
输入 #2复制
5
2 3 5 0
1 4 5 3 0
1 2 5 0
1 2 3 0
4 3 2 1 0
输出 #2复制
2 3
说明/提示
【数据规模】

对于 30% 的数据,N<=10;

对于 100% 的数据,N<=2000。


二分图判定+背包。

我们对厌烦的关系建图,只要图不是二分图那么就无解,因为存在3个人两两之间不喜欢。

不然我们就相当于是把染白色或者染黑色的加入答案。因为差距尽量小,所以这一部分做背包即可。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e3+10;
int n,g[N][N],col[N],sum[3],flag,V,a[N],cnt,dp[N],tv,base;
vector<int> v[N];
void dfs(int x,int c){
	col[x]=c;	sum[c]++;
	for(int i=0;i<v[x].size();i++){
		int to=v[x][i];
		if(col[to]&&col[to]==c)	return void(flag=1);
		if(!col[to])	dfs(to,c^3);
	}
}
signed main(){
	cin>>n;
	for(int i=1,x;i<=n;i++){
		do	scanf("%d",&x),g[i][x]=1;	while(x);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++)	
			if(!g[i][j]||!g[j][i])	v[i].push_back(j),v[j].push_back(i);
	}
	for(int i=1;i<=n&&!flag;i++){
		if(col[i])	continue;	sum[1]=sum[2]=0;	dfs(i,1);	
		a[++cnt]=max(sum[1],sum[2])-min(sum[1],sum[2]);	V+=a[cnt]; 
		base+=min(sum[1],sum[2]);
	}
	if(flag)	return puts("No solution"),0; tv=V/2;
	for(int i=1;i<=cnt;i++)
		for(int j=tv;j>=a[i];j--)	dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
	cout<<dp[tv]+base<<' '<<n-dp[tv]-base;
	return 0;
}
发布了416 篇原创文章 · 获赞 228 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43826249/article/details/103929211