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

题意: 有 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;
}

 

posted @ 2012-08-24 19:56  'wind  阅读(1212)  评论(0编辑  收藏  举报