题目链接:点击打开链接
题目大意: 给出n个人,每个人都有认识的人,然后是否能分成两组,每组的人都相互认识,求两组最小人数差,不过不能分成两组,输出no solvnion。
题目思路:一开始是挺没有思路的,如果仔细分析的话可以分析出,对于,每一个不相互认识的人可以看成二分图,因为只能分成两种人,一种相互认识,一种不相互认识,
所以的话,我们把不相互认识的建图,有边存在的一定不能放在同一个集合中,这样的话构建出来的图是一个一个联通块,对于每一个联通块来说,如果不是二分图,肯定是不合法的,判定是否合法之后,我们的问题就是怎么找到差值最小的安排方式,经过前面的分析,我们可以发现每一个联通块分为两个绑定的团体,不同联通块则没有关系,这样的话,我们可以想到用dp去解决,设状态dp[i][j],为前i个联通块,第一个比第二个多j个是否存在,为什么呢,一般我们都是设差的绝对值,这里因为要打印路径,我们设为这样,为了方便打印路径,或者设为dp[i][j][k],为前i个联通块中第一个为j个,第二个为k个,剩下的问题就是打印路径了,因为每一个联通块的两种人只能选择是去第一个或则者去第二个,这样的话,给我们打印路径提供了很大的方便,从最后的答案逆推回去,根据这两种选择看上一个上一个状态是否存在,然后记录下来就行了。
ac代码;
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstring>
#include<iostream>
#include<sstream>
#include<cmath>
#include<vector>
#define LL long long
#define INF 0x3f3f3f3f
#define eps 1e-6
using namespace std;
int T;
int mp[105][105];
int head[105];
int tot;
int n;
int cntt;
int color[105];
int diff[105];
int dp[105][205];
vector<int>G[105][2];
struct node
{
int u,v;
int net;
}E[105*105];
int vis[105];
void __init__()
{
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
memset(mp,0,sizeof(mp));
memset(color,-1,sizeof(color));
memset(diff,0,sizeof(diff));
memset(dp,0,sizeof(dp));
tot = 0;
cntt = 0;
for(int i = 0;i<n;i++){
G[i][0].clear();
G[i][1].clear();
}
}
void build(int u,int v)
{
E[tot].u =u;
E[tot].v = v;
E[tot].net = head[u];
head[u] = tot++;
}
int dfs(int u,int clo)
{
color[u] = clo;
vis[u] = 1;
G[cntt][clo].push_back(u);
for(int i = head[u];~i;i = E[i].net){
int to = E[i].v;
if(u==to)
continue;
if(color[to]==color[u])
return 0;
if(color[to]==-1&&!dfs(to,clo^1))
return 0;
}
return 1;
}
void print(int ans)
{
vector<int>ans1;
vector<int>ans2;
for(int i =cntt-1;i>=0;i--){
int t;
if(dp[i][ans-diff[i]+n]){
t = 0;
ans-=diff[i];
}
else{
t = 1;
ans+=diff[i];
}
int len = G[i][t].size();
for(int j = 0;j<len;j++)
ans1.push_back(G[i][t][j]);
len = G[i][t^1].size();
for(int j = 0;j<len;j++){
ans2.push_back(G[i][t^1][j]);
}
}
int len1 = ans1.size();
int len2 = ans2.size();
if(len1==0||len2==0){
puts("No solution");
return ;
}
cout<<len1<<' ';
for(int i = 0;i<len1;i++)
{
printf("%d%c",ans1[i],i==len1-1?'\n':' ');
}
cout<<len2<<' ';
for(int i = 0;i<len2;i++){
printf("%d%c",ans2[i],i==len2-1?'\n':' ');
}
}
void dpp()
{
dp[0][n] = 1;
for(int i = 0;i<cntt;i++){
for(int j = -n;j<=n;j++)
{
if(dp[i][j+n]){
dp[i+1][j-diff[i]+n] = 1;
dp[i+1][j+diff[i]+n] = 1;
}
}
}
for(int i = 0;i<=n;i++){
if(dp[cntt][i+n]){
print(i);
return ;
}
if(dp[cntt][-i+n]){
print(i);
return ;
}
}
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
__init__();
for(int i = 1;i<=n;i++){
while(1){
int x;
scanf("%d",&x);
if(!x)
break;
mp[i][x] = 1;
}
}
for(int i = 1;i<=n;i++){
for(int j = i+1;j<=n;j++){
if(mp[i][j]==0||mp[j][i]==0)
{
build(i,j);
build(j,i);
}
}
}
int falg = 0;
for(int i = 1;i<=n;i++){
if(vis[i]==0&&color[i]==-1){
if(!dfs(i,0)){
falg = 1;
break;
}
else
{
diff[cntt] = G[cntt][0].size()-G[cntt][1].size();
cntt++;
}
}
}
if(falg)
puts("No solution");
else{
dpp();
}
if(T)
puts("");
}
return 0;
}