您的位置:首页 > 其它

POJ 1112 Team Them Up!【二分图染色+DP】

2012-08-24 19:56 393 查看
题意: 有 n 个人,他们之间的关系有四种,给出一些关系 a,b 表示b 知道 a,现在想把这些人分成两组,每个组里面所有人都相互知道,如果可以分成这两组,

找出两组人数相差最少的情况。

分析:如果 a 和 b 不是相互知道,就在a,b之间连一条双向边,表示a 和b 绝不能分在一个组里

建好图之后,进行染色,判断是否是二分图,如果不是二分图,肯定不存在符合条件的情况

染色的同时,记录每个连通块中每个部分的个数,并记录路径

用 01 背包标记所有存在的状态,找到差值最小的情况

#include<stdio.h>
#include<string.h>
#define clr(x)memset(x,0,sizeof(x))
#define maxn 110
int g[maxn][maxn];
int r[maxn][maxn];
int dp[maxn][maxn];
int c[maxn];
int num[maxn][2];
int f[maxn];
int flag;
int sn,d,n;
void dfs(int x)
{
int i;
for(i=1;i<=n;i++)
if(r[x][i])
{
if(c[x]==c[i])
{
flag=1;
return;
}
else if(c[i]!=-1)
continue;
f[i]=sn;
c[i]=c[x]^1;
num[sn][c[i]]++;
dfs(i);
f[i]=sn;
if(flag==1)
return;
}
}
int main()
{
int i,j,p;
while(scanf("%d",&n)!=EOF)
{
clr(g);
clr(r);
clr(f);
clr(num);
clr(f);
clr(dp);
memset(c,-1,sizeof(c));
for(i=1;i<=n;i++)
{
while(scanf("%d",&p),p)
g[i][p]=1;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
if(g[i][j]&&g[j][i])
continue;
if(i==j)
continue;
r[i][j]=r[j][i]=1;
}
sn=1;
flag=0;
for(i=1;i<=n;i++)
if(c[i]==-1)
{
c[i]=0;
num[sn][0]++;
f[i]=sn;
dfs(i);
if(flag==1)
{
printf("No solution\n");
break;
}
sn++;
}
if(flag)
return 0;
dp[0][0]=1;
for(i=1;i<sn;i++)
for(j=0;j<=n/2;j++)
{
if(dp[i-1][j])
{
if(j+num[i][0]<=n/2)
dp[i][j+num[i][0]]=1;
if(j+num[i][1]<=n/2)
dp[i][j+num[i][1]]=1;
}
}
int tmp,cn;
for(i=n/2;i>=0;i--)
{
if(dp[sn-1][i]==1)
{
tmp=i;
cn=i;
break;
}
}
int vis[maxn];
clr(vis);
for(i=sn-1;i>=1;i--)
{
int tt;
if(tmp-num[i][0]>=0&&dp[i-1][tmp-num[i][0]])
{
tt=0;
tmp=tmp-num[i][0];
}
else if(tmp-num[i][1]>=0&&dp[i-1][tmp-num[i][1]])
{
tmp=tmp-num[i][1];
tt=1;
}
for(j=1;j<=n;j++)
if(f[j]==i&&c[j]==tt)
vis[j]=1;
}
printf("%d ",cn);
for(i=1;i<=n;i++)
if(vis[i])
printf("%d ",i);
putchar('\n');
printf("%d ",n-cn);
for(i=1;i<=n;i++)
if(!vis[i])
printf("%d ",i);
putchar('\n');
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: