18pts!玄关!
查看原帖
18pts!玄关!
1314528
Oscar111111楼主2025/7/31 20:01
/*
解题思路:
1.把没有关系的队员连线
2.用 黑-白-黑-白 的方式染色每一个联通块
3.01背包来选出那些在A组
*/
#include<bits/stdc++.h>
using namespace std;
const int N=1e2+10;
bool dp[N][N],know[N][N],book[N];
int colour[N],r[N][N],head[N][N];
int sum,ans,n,ar;
vector<int>vec[N][3];//vecc[i][j] : i联通块j组(颜色)
void dfs(int x,int fa,int c){//染色
	colour[x]=c;
	vec[sum][c].push_back(x);
	for(int i=1;i<=n;i++){
		if(!know[x][i]&&i!=x&&i!=fa){//都认识
			if(!colour[i]) dfs(i,x,3-c);
			else{
				if(colour[i]==c){//以 黑-白-黑-白 染色,所以如果一样就输出No solution
					cout<<"No solution";
					exit(0);
				}
			}
		}
	}
}
void bag01(){//dp
	dp[0][0]=1;
	//dp[i][j] : 在前i个联通块中选,能否使A组的人数>=j
	for(int i=1;i<=sum;i++){
		for(int j=1;j<=n;j++){
			if(j>=vec[i][0].size()&&dp[i-1][j-vec[i][0].size()])//如果上一个有方法
				dp[i][j]=1,r[i][j]=1,head[i][j]=j-vec[i][0].size();
			if(j>=vec[i][1].size()&&dp[i-1][j-vec[i][1].size()])//如果上一个有方法
				dp[i][j]=1,r[i][j]=2,head[i][j]=j-vec[i][1].size();
		}
	}
	for(int i=n/2;i>=1;i--){//因为要平均,所以倒着枚
		if(dp[sum][i]){
			ans=i;
			return;
		}
	}
}
int main(){
	ios::sync_with_stdio(false); // 关闭与 C 标准库的同步
	cin.tie(nullptr); // 解除 cin 和 cout 的绑定
	cout.tie(nullptr); // 可选,进一步优化输出
	cin>>n;
	for(int i=1;i<=n;i++){
		while(cin>>ar){
			if(ar==0) break;
			else know[i][ar]=1;
		}
	}
	for(int i=1;i<=n;i++){
		if(!colour[i]){
			sum++;
			dfs(i,0,1);
		}
	}
	bag01();
	cout<<ans<<" ";
	int a=sum,b=ans;
	while(a&&b){
		for(int i=0;i<vec[a][r[a][b]].size();i++){
			book[vec[a][r[a][b]][i]]++;
		}
		b=head[a--][b];
	}
	for(int i=1;i<=n;i++)
		if(book[i]) cout<<i<<" ";
	cout<<endl<<n-ans<<" ";
	for(int i=1;i<=n;i++)
		if(!book[i]) cout<<i<<" ";
	return 0;
}
2025/7/31 20:01
加载中...