题意:有N个人要分成两组,已知每个人认识哪些人,限制是组内人员必须互相认识,要求输出一组两组人数差最小的分配方案。
思路:这道题稍微有点麻烦。要经过一下几个步骤的处理:
1.各个点之间,将只有互相认识的点连线,然后求补图。这样做方便找出相互认识的几个集合。这几个集合间可以随意组合,因为不同集合里的人是相互认识的。于是问题就变成求每个集合里人相互认识的问题了。
2.求每个集合里人相互认识的情况:这里反正就用一遍广度优先搜索,因为有边相连的肯定是互不认识的。其实只要用一次广搜,不仅把各个集合给分开了,集合内的二分染色问题也解决了。不过碰到不能两种颜色染好的,就gameover。
3.用动态规划,其实就是一道0,1背包问题.动态规划公式如下:
int tt=j-s[i].n1;
if(tt>=0&&dp[i-1][tt])
{
dp[i][j]=1;//置1表示可以得到这种情况
}
tt=j-s[i].n2;
if(tt>=0&&dp[i-1][tt])
{
dp[i][j]=1;
}
上面i表示第i个集合,j表示所拥有的元素数量,s[i].n1表示第i个集合中,第一种分类元素个数。s[i].n2表示第i个集合中,第二种分类元素个数。注意1<=j<=n/2,因为最后所求是要两个集合元素个数相近,又因为必有一个集合的元素个数<=n/2,所以最后就j由大往小搜索,搜到最大的可行j,即可。
#include<cstdio>
#include<cstring>
#include<vector>
#include<iterator>
#include<iostream>
#include<queue>
using namespace std;
int map[150][150],mp[150][150],vis[150],cnt,color[150];
struct node
{
int n1,n2;
int a[105],b[105];
}s[105];
int pre[105][105],dp[105][105],pre2[105][105];
struct node2
{
int num,color;
}q[105];
queue<node2> p;
int main()
{
int n,t;
//FILE *fp=fopen("t.txt","r");
scanf("%d",&n);
memset(map,0,sizeof(map));
memset(mp,0,sizeof(mp));
for(int i=1;i<=n;i++)
{
while(1)
{
scanf("%d",&t);
if(t==0)
break;
map[i][t]=1;
}
}
for(int i=1;i<=n-1;i++)
{
for(int j=i+1;j<=n;j++)
{
if(map[i][j]==1&&map[j][i]==1)
{
mp[i][j]=mp[j][i]=1;
}
}
}
memset(vis,0,sizeof(vis));
while(!p.empty())
{
p.pop();
}
node2 tp;
cnt=1;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
q[i].num=i; q[i].color=0;
vis[i]=cnt;
s[cnt].n1=s[cnt].n2=0;
p.push(q[i]);
while(!p.empty())
{
tp=p.front();
p.pop();
if(!tp.color)
{
s[cnt].a[s[cnt].n1]=tp.num;
s[cnt].n1++;
}
else
{
s[cnt].b[s[cnt].n2]=tp.num;
s[cnt].n2++;
}
for(int j=1;j<=n;j++)
{
if(!mp[j][tp.num]&&!vis[j])
{
vis[j]=cnt;
q[j].num=j; q[j].color=tp.color^1;
p.push(q[j]);
}else if(!mp[j][tp.num]&&vis[j])
{
if(tp.color==q[j].color&&tp.num!=q[j].num)
{
printf("No solution\n");
return 0;
}
}
}
}
cnt++;
}
}
/******************dp*******************************/
int tt;
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<cnt;i++)
{
for(int j=1;j<=n/2;j++)
{
tt=j-s[i].n1;
if(tt>=0&&dp[i-1][tt])
{
dp[i][j]=1;
pre[i][j]=0;
pre2[i][j]=tt;
}
tt=j-s[i].n2;
if(tt>=0&&dp[i-1][tt])
{
dp[i][j]=1;
pre[i][j]=1;
pre2[i][j]=tt;
}
}
}
int r;
for(int i=n/2;i>=1;i--) if(dp[cnt-1][i]){r=i;break;}
//cout<<r<<endl;
int step1[101],step2[101],cnt1=0,cnt2=0;
for(int i=cnt-1;i>=1;i--)
{
if(!pre[i][r])
{
for(int j=0;j<s[i].n1;j++)
step1[cnt1++]=s[i].a[j];
for(int j=0;j<s[i].n2;j++)
step2[cnt2++]=s[i].b[j];
}else
{
for(int j=0;j<s[i].n2;j++)
step1[cnt1++]=s[i].b[j];
for(int j=0;j<s[i].n1;j++)
step2[cnt2++]=s[i].a[j];
}
r=pre2[i][r];
}
printf("%d ",cnt1);
for(int i=0;i<cnt1;i++)
printf("%d ",step1[i]);
printf("\n");
printf("%d ",cnt2);
for(int i=0;i<cnt2;i++)
printf("%d ",step2[i]);
return 0;
}