题意:
给出n个人,和一些关系有向的关系,有向边a->b表示a认识b:
将n个人划分为两组,要求每组组内人必须互相都认识,同时要求两组人数差最小。
思路:
可以知道的是必须有a->b,且b->a的关系存在时,a和b才能被分为一组。考虑反向建图,若a,b不能分为一组,则连一条a,b之间的无向边。现在就变成了一系列连通块,对于他们去做二分图奇偶染色,将每个连通块根据奇偶划分为两部分,这两部分必然不能在一个集合,然后在这一系列的块中,每个连通块选一部分,形成总的集合a,剩下的点作为集合b,使用dp求取选择的最优值。若在此过程中,存在一个连通块不是二分图,那么一定无解。这个操作也可以通过带权并查集实现,维护每个连通块中的点到parent的关系,此关系其实也就是二分图中的奇偶关系。
代码:
代码略丑。。。
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int size = 200;
int n;
int mp[size][size];
bool vis[size];
vector<int> con[size][2];
int cnt;
int dp[size][size];
int st[size][size];
int pr[size][size];
int se[size];
vector<int> at;
bool dfs(int a,int so,int pre)
{
if(vis[a])
{
if(se[a] != so)
return false;
else
return true;
}
se[a] = so;
vis[a] = true;
con[cnt][so].push_back(a);
for(int i = 1; i <= n;i++)
{
if(pre==i || i == a) continue;
if(mp[i][a] == 0)
{
if(!dfs(i,1-so,a)) return false;
}
}
return true;
}
int main()
{
int t;
scanf("%d",&t);
//t = 1;
while(t--)
{
cnt = 0;
for(int i = 0; i < size;i++)
for(int j = 0; j < 2; j++)
con[i][j].clear();
memset(mp,0,sizeof(mp));
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
int ms;
while(~scanf("%d",&ms)&&ms)
mp[i][ms] = 1;
mp[i][i] = 1;
}
for(int i = 1;i <= n; i++)
{
for(int j = 1;j <= n; j++)
{
if(mp[i][j] == 0 || mp[j][i] == 0)
mp[i][j] = mp[j][i] = 0;
//printf(" %d",mp[i][j]);
}
//printf("\n");
}
memset(vis,false,sizeof(vis));
bool anss = true;
for(int i =1;i <= n; i++)
if(!vis[i])
{
anss = anss &&dfs(i,0,-1);
cnt++;
}
if(!anss)
{
printf("No solution\n");
if(t!=0)
printf("\n");
continue;
}
memset(dp,0,sizeof(dp));
dp[0][con[0][0].size()] = 1;
dp[0][con[0][1].size()] = 1;
st[0][con[0][0].size()] = 0;
st[0][con[0][1].size()] = 1;
for(int i = 1; i <cnt;i++)
{
int a = con[i][0].size();
int b = con[i][1].size();
for(int j = 0;j <= n;j++)
{
bool can = false;
if(j - a >= 0&&dp[i-1][j-a])
{
can = true;
pr[i][j] = j-a;
st[i][j] = 0;
}
if(j - b >= 0 && dp[i-1][j-b])
{
can = true;
pr[i][j] = j-b;
st[i][j] = 1;
}
if(can)
dp[i][j] = 1;
}
}
int mis = 2e9;
int ans;
for(int j = 1; j < n; j++)
{
if(dp[cnt-1][j])
{
int dis = n - j -j;
if(dis < 0) dis = -dis;
if(dis <mis)
{
mis = dis;
ans = j;
}
}
}
memset(vis,false,sizeof(vis));
at.clear();
for(int i = cnt-1; i >= 0; i--)
{
for(int j = 0; j < con[i][st[i][ans]].size();j++)
{
int mks = con[i][st[i][ans]][j];
at.push_back(mks);
vis[mks] = true;
}
ans = pr[i][ans];
}
printf("%d",n-at.size());
for(int i = 1;i <= n; i++)
if(!vis[i])printf(" %d",i);
printf("\n");
printf("%d",at.size());
for(int i = 0;i < at.size(); i++)
printf(" %d",at[i]);
printf("\n");
if(t!= 0)printf("\n");
}
return 0;
}