图论专题(一)POJ1112二分染色+DP

17 篇文章 0 订阅
//题目大意:给定一个有向图,表示N个人之间的认识关系.现在需要把N个人分成两个team.
//要求每个team中的人必须互相认识.同时要求两个team的人数相差最少.
//题目连接:http://poj.org/problem?id=1112
//解题思路:把现有的建立的有向图求其补图.同时将有向边变成无向边.那么新构成的图G';
//G'的意义是:a-b有边表示a和b不能在一个team里面
//接下来对G'的每个连通分量进行2分染色.同时记录下每个分量染色为0和染色为1的数目.
//no solution的情况就是染色不成功.即出现一个点既不能是染色0.也不能是染色1;
//最后就是用dp处理两个team人数相差最少.
//dp[i][j]表示到第i个连通分量能否得到含有j个人数的team

//dp[i][j] = (dp[i-1][j - 第i个中染色为0的数目] || dp[i-1][j- 第i个中染色为1的数目])

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
using namespace std;
const int mn = 111;
int rank[mn],father[mn],rs[mn],connum[mn][5],n,map[mn][mn],dp[mn][mn],cas[mn][mn];
int ans[mn][mn],ps[mn][mn];
vector<int>conne[mn];
void init(int x)
{
    for(int i = 1 ; i <= x ; i++)
    {
        rank[i] = 0;
        father[i] = i;
    }
}
int find(int x)
{
    if(x != father[x])
        father[x] = find(father[x]);
    return father[x];
}
void Union(int x , int y)
{
    x = find(x);
    y = find(y);
    if(x == y)return ;
    else
    {
        if(rank[x] < rank[y])father[x] = y;
        else
        {
            if(rank[x] == rank[y])rank[x]++;
            father[y] = x;
        }
    }
}
bool DFS(int i , int x , int ys)
{
    vector<int>::iterator it;
    for(it = conne[i].begin() ; it < conne[i].end()  ; it++)
    {
        if(map[x][*it])
        {
            if(rs[*it] == -1)
            {
                rs[*it] = 1 - ys;
                if(!DFS(i, *it , rs[*it]))return 0;
            }
            else if(rs[*it] != 1 - ys)return 0;
        }
    }
   // if(!ppm)return 0;
    return 1;
}
int main()
{
    while(scanf("%d",&n) != EOF)
    {
        for(int i = 1 ; i <= n ; i++)conne[i].clear();
        for(int i = 1 ; i <= n ; i++)
          for(int j = 1 ; j <= n ; j++)
          {
             if(i == j)map[i][j] = 0;
             else map[i][j] = 1;
          }
        int x;
        memset(cas,0,sizeof(cas));
        for(int i = 1 ; i <= n ; i++)
          while(scanf("%d",&x) && x != 0)
          {
             cas[i][x] = 1;
          }
        for(int i = 1 ; i <= n ; i++)
        {
            for(int j = 1 ; j <= n ; j++)
            {
                if(i == j )continue;
                if(cas[i][j] && cas[j][i])
                {
                    map[i][j] = 0;
                    map[j][i] = 0;
                }
            }
        }
        init(n);
        for(int  i = 1 ; i <= n ; i++)rs[i] = -1;
        for(int i = 1 ; i <= n ; i++)
          for(int j = 1 ; j <= n ; j++)
            if(map[i][j])Union(i,j);
        int t = 1;
        for(int i = 1 ; i <= n ; i++)
        {
            bool vis = 1;
            int p ;
            for(int j = 1 ; j < t ; j++)
             if(find(i) == find( *(conne[j].begin()) ))
             {
                 vis = 0;
                 p = j;
             }
            if(!vis)conne[p].push_back(i);
            else conne[t++].push_back(i);
        }
        bool pp = 1;
        memset(rs,-1,sizeof(rs));
        for(int i = 1 ; i < t ; i++)
        {
            rs[*(conne[i].begin())] = 0;
            if(!DFS( i,*(conne[i].begin()) , 0) )pp = 0;
        }

        if(!pp)printf("No solution\n");
        else
        {
            for(int i = 1 ; i <= n ; i++)if(rs[i] == -1)while(1);
            memset(connum,0,sizeof(connum));
            for(int i = 1 ; i < t ; i++)
            {
                vector<int>::iterator it;
                for(it = conne[i].begin() ; it < conne[i].end() ; it++)
                  connum[i][rs[*it]]++;
            }
            memset(dp,0,sizeof(dp));
            dp[1][connum[1][0]] = 1;
            ans[1][connum[1][0]] = 0;
            dp[1][connum[1][1]] = 1;
            ans[1][connum[1][1]] = 1;
            for(int i = 2 ; i < t ; i++)
            {
                for(int j = 1 ; j <= n ; j++)
                {
                    if(j >= connum[i][0] && dp[i-1][j - connum[i][0]])
                    {
                        dp[i][j] = 1;
                        ans[i][j] = 0;
                    }
                    if(j >= connum[i][1] && dp[i-1][j - connum[i][1]])
                    {
                        dp[i][j] = 1;
                        ans[i][j] = 1;
                    }
                }
            }
            int res = 0;
            for(int i = 1 ; i*2 <= n ; i++)
              if(dp[t-1][i] && abs(i - n/2) < abs( res - n /2))
               res = i;
            bool flag = 1;
            int mm = 1;
            int tt = 1;
            t--;
            while(flag)
            {
                vector<int>::iterator it;
                if(ans[t][res])
                  for(it = conne[t].begin() ; it < conne[t].end() ; it++)
                  {
                      if(rs[*it]) {res--;ps[1][mm++] = *it;}
                      else ps[2][tt++] = *it;
                  }
                else
                  {
                      for(it = conne[t].begin() ; it < conne[t].end() ; it++)
                        if(rs[*it])ps[2][tt++] = *it;
                        else {res--;ps[1][mm++] = *it;}
                  }
                  t--;
                  if(!t)flag = 0;
            }
          //  printf("%d\n",res);
            printf("%d ",tt-1);
            for(int i = 1 ; i < tt-1 ; i++)printf("%d ",ps[2][i]);
            printf("%d\n",ps[2][tt-1]);
            printf("%d ",mm-1);
            for(int i = 1 ; i < mm-1 ; i++)printf("%d ",ps[1][i]);
            printf("%d\n",ps[1][mm-1]);
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值