队员分组 (二分图+dp--差值最小问题)

有 nn 个人从 11 至 nn 编号,相互之间有一些认识关系,你的任务是把一些人分成两组,使得: 每个人都被分到其中一组。 每个组都至少有一个人。 一组中的每个人都认识其他同组成员。 在满足上述条件的基础上,要求两组成员的人数之差(绝对值)尽可能小。请构造一种可行的方案。 请注意,xx 认识 yy 不一定说明 yy 认识 xx;xx 认识 yy 且 yy 认识 zz 不一定说明 xx 认识 zz。即认识关系是单向且不可传递的。 输入格式 输入的第一行是一个整数,代表总人数 nn。 第 22 到第 (n + 1)(n+1) 行,每行有若干个互不相同的整数,以 00 结尾,第 (i + 1)(i+1) 行的第 jj 个整数 a_{i, j}a i,j (00 除外)代表第 ii 个人认识 a_{i, j}a i,j 。 输出格式 本题存在 Special Judge。 如果无解,请输出一行一个字符串 No solution。 如果有解,请输出两行整数,分别代表两组的成员。每行的第一个整数是该组的人数,后面以升序若干个整数代表该组的成员编号,数字间用空格隔开。 输入输出样例 输入 #1复制 5 2 3 5 0 1 4 5 3 0 1 2 5 0 1 2 3 0 4 3 2 1 0 输出 #1复制 3 1 3 5 2 2 4 说明/提示 数据规模与约定 对于全部的测试点,保证 2 \leq n \leq 1002≤n≤100,1 \leq a_{i, j} \leq n1≤a i,j ≤n。
思路
- 遇到分为2个组的时候,一般要考察二分图,特别是遇到2个点不能在一起的时候,就一定是二分图
- 具体如何转化到2个点不能在一起,更具题目具体分析
- 通过二分图,可以分为很多个连通板块,每一个连通板块可以分为 2种嘛(二分图,用2和3表示2种类型颜色(位运算^))
- 关键是DP, 这里是让他们之间的差值最小,而且他们 2个数的和 是定值 n,
- 因此只需要看看 连通块门,可以组合出多少类型数量(在一组内),从而另一组的数目也就知道,
- 这相当于分组dp,dp【i】【j】=1表示这个数可以连上,
- 想当重要: 如果 当前连通块只有1个数,那么他的类型3就是0,这个0也可以进行更新,特别重要!!!!(可以把特殊情况个解决!)
- 应为这个背包,是用的二维数组,就可以回溯,

#include <bits/stdc++.h> using namespace std; #define M 105 #define ri register int template <class G> void read(G &x) { x=0;int f=0;char ch=getchar(); while(ch<'0'||ch>'9'){f|=ch=='-';ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x=f?-x:x; return ; } int n,m; int arr[M][M]; vector <int> p[M]; vector <int> q[M][M]; int tmp=0; int flag[M]; int vis[M]; void dfs(int a) { vis[a]=1; q[tmp][flag[a]].push_back(a); for(ri i=0;i<p[a].size();i++) { int b=p[a][i]; if(flag[b]==flag[a]) { printf("No solution"); exit(0); } flag[b]=flag[a]^1; if(vis[b]) continue; dfs(b); } } int dp[M][M]; int pr[M][M]; int ANS1[M],ANS2[M]; int main(){ read(n); for(ri i=1;i<=n;i++) { while(1) { int a; read(a);if(a==0) break; arr[i][a]=1; } } for(ri i=1;i<=n;i++) { for(ri j=i+1;j<=n;j++) { if(!arr[i][j]||!arr[j][i]) { p[i].push_back(j); p[j].push_back(i); } } } for(ri i=1;i<=n;i++) { if(!flag[i]) { ++tmp; flag[i]=2; dfs(i); } } dp[0][0]=1; for(ri i=1;i<=tmp;i++) { for(ri j=0;j<=n/2;j++) { int t=j-q[i][2].size(); if(t>=0) { if(dp[i-1][t])dp[i][j]=1,pr[i][j]=2; } t=j-q[i][3].size(); if(t>=0) { if(dp[i-1][t]) { dp[i][j]=1;pr[i][j]=3; } } } } int ans=0; for(ri i=n/2;i>=1;i--) { if(dp[tmp][i]) { ans=i;break; } } int ff=ans; int cur=n/2; for(ri i=tmp;i>=1;i--) { int a=pr[i][ans]; for(ri j=0;j<q[i][a].size();j++) { int b=q[i][a][j]; ANS1[b]=1; } ans-=q[i][a].size(); } for(ri i=1;i<=n;i++) { if(ANS1[i]) continue; ANS2[i]=1; } printf("%d ",ff); for(ri i=1;i<=n;i++) { if(ANS1[i]) printf("%d ",i); } printf("\n"); printf("%d ",n-ff); for(ri i=1;i<=n;i++) { if(ANS2[i]) printf("%d ",i); } return 0; }