POJ 1112 Team Them Up

Team Them Up!
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 3404 Accepted: 901 Special Judge

Description

Your task is to divide a number of persons into two teams, in such a way, that: 

everyone belongs to one of the teams; 

every team has at least one member; 

every person in the team knows every other person in his team; 

teams are as close in their sizes as possible. 

This task may have many solutions. You are to find and output any solution, or to report that the solution does not exist.

Input

For simplicity, all persons are assigned a unique integer identifier from 1 to N. 

The first line in the input file contains a single integer number N (2 <= N <= 100) - the total number of persons to divide into teams, followed by N lines - one line per person in ascending order of their identifiers. Each line contains the list of distinct numbers Aij (1 <= Aij <= N, Aij != i) separated by spaces. The list represents identifiers of persons that ith person knows. The list is terminated by 0.

Output

If the solution to the problem does not exist, then write a single message "No solution" (without quotes) to the output file. Otherwise write a solution on two lines. On the first line of the output file write the number of persons in the first team, followed by the identifiers of persons in the first team, placing one space before each identifier. On the second line describe the second team in the same way. You may write teams and identifiers of persons in a team in any order.

Sample Input

5
2 3 5 0
1 4 5 3 0
1 2 5 0
1 2 3 0
4 3 2 1 0

Sample Output

3 1 3 5
2 2 4

Source

 

/*

非常经典的一道DP(0,1背包)题,一个小小的错误让我调了几个小时,思路如下:

 

1)首先对原图求补图,只有当原图中两个人i和j不同时认识时,在补图中rever[i][j] = rever[j][i] = true, 否则为false;

这样做非常巧妙,通过这个转换将陌生的问题转换为了熟悉的问题

2)利用dfs对补图求所有的连通分量,这样很容易看出,处于不同连通分量中的两个点肯定是可以组在一个队中的;在用dfs

求连通分量的同时对同一个连通分量中的点进行0,1着色,这样当求得一个连通分量后,这个连通分量就分成了d0和d1两组

点。这时候需要做一个判断,对于同一个连通分量中的两个点i和j,如果i和j的0,1着色相同且在补图中i和j之间相连,则此题

肯定无解(花个图很容易就明白了,很好证明)

3)接下来的工作就很熟悉了,相当于将所有连通分量里面的0和1着色组分别分配到1队和2队,里,是1队和2队之间的人数差最小。

这是个典型的0,1背包问题,第i个连通分量里的0,1着色组的数目分别表示为con(i, 0)和con(i, 1),则DP公式如下

 

f[c][curDiff + con[c][0] - con[c][1]] = true if f[c - 1][curDiff] = true;

f[c][curDiff - con[c][0] + con[c][1]] = true if f[c - 1][curDiff] = true;

dp的同时需要记录路径

 

dp完后,找出f[lastC]中绝对值最小的,然后往前推就可以得出两队各自的人数,并输出id即可

*/

 

#include <iostream>
#include <memory>
#include <cmath>
#define MAX_N 102
using namespace std;

static bool input[MAX_N + 2][MAX_N + 2];
static bool rever[MAX_N + 2][MAX_N + 2];
static bool visited[MAX_N + 2];
static int con[MAX_N + 2][MAX_N + 2][2];  // WHICH, COLOR
static int count[MAX_N + 2][3]; //TOTAL NUM, 0 NUM, 1 NUM 2
static int dpR[MAX_N + 2][MAX_N * 2 + 2];
static int path[MAX_N + 2][MAX_N * 2 + 2][2];
int n, conNum = 0;

int finalRes[2][MAX_N + 2];
int total0 = 0 , total1 = 0;

void init()
{
    memset(input, false, sizeof(input));
    memset(rever, false, sizeof(rever));
    memset(visited, false, sizeof(visited));
    memset(con, 0, sizeof(con));
    memset(count, 0, sizeof(count));
    memset(dpR, 0, sizeof(dpR));
    memset(path, 0, sizeof(path));
    memset(finalRes, 0, sizeof(finalRes));
}
bool exist(int curSeq, int diffVal)
{
    int num = dpR[curSeq][0];
    for(int i = 1; i <= num; i++)
        if(dpR[curSeq][i] == diffVal)
            return true;
    return false;
}
void dpSolve()
{
    int p, s;
    dpR[0][0] = 1;
    for(p = 0; p < conNum; p++)
    {
        for(s = 1; s <= dpR[p][0]; s++)
        {
            int curDiff = dpR[p][s];
            int newD1 = curDiff + count[p + 1][1] - count[p + 1][2];
            int newD2 = curDiff - count[p + 1][1] + count[p + 1][2];

            if(!exist(p + 1, newD1))           
            {
                dpR[p + 1][0]++;
                dpR[p + 1][dpR[p + 1][0]] = newD1;
                path[p + 1][dpR[p + 1][0]][0] = 0;
                path[p + 1][dpR[p + 1][0]][1] = s;
            }
            if(!exist(p + 1, newD2))
            {
                dpR[p + 1][0]++;
                dpR[p + 1][dpR[p + 1][0]] = newD2;
                path[p + 1][dpR[p + 1][0]][0] = 1;
                path[p + 1][dpR[p + 1][0]][1] = s;
            }
        }
    }
}

void dfs(int conSeq, int preColor, int curN)
{
    visited[curN] = true;
    int where = ++count[conSeq][0];
    con[conSeq][where][0] = curN;
    con[conSeq][where][1] = 1 - preColor;
    //((1 - preColor) == 0) ? count[conSeq][1]++ : count[conSeq][2]++;
    if((1 - preColor) == 0)
        count[conSeq][1]++;
    else
        count[conSeq][2]++;


    for(int i = 1; i <= n; i++)
    {
        if(i == curN || rever[curN][i] != 1 || visited[i])
            continue;
        dfs(conSeq, 1 - preColor, i);
    }
}
bool dfsTag()
{
    int i, j, k;
    for(i = 1; i <= n; i++)
    {
        if(!visited[i])
        {
            conNum++;
            dfs(conNum, 1, i);
            int nNum = count[conNum][0];
            for(j = 1; j <= nNum; j++)
            {
                for(k = 1; k <= nNum; k++)
                {
                    if(j == k)
                        continue;
                    int n1 = con[conNum][j][0];
                    int n2 = con[conNum][k][0];
                    if(con[conNum][j][1] == con[conNum][k][1] && rever[n1][n2] == 1)
                    {
                        cout<<"No solution"<<endl;
                        return false;
                    }
                }
            }
        }
    }
    return true;
}
void getRever()
{
    int i, j;
    for(i = 1; i <= n; i++)
    {
        for(j = 1; j <= n; j++)
        {
            if(i == j)
                continue;
            if(!(input[i][j] && input[j][i]))
                rever[i][j] = rever[j][i] = true;
        }
    }
}

void getFinalRes(int curCon, int curDiffPos)
{
    if(curCon == 0)
        return;
    int type = path[curCon][curDiffPos][0];
   
    if(type == 0)
    {
        total0 += count[curCon][1];
        total1 += count[curCon][2];
       
        finalRes[0][curCon] = 0;
        finalRes[1][curCon] = 1;
   
    }
    else
    {
        finalRes[1][curCon] = 0;
        finalRes[0][curCon] = 1;

        total1 += count[curCon][1];
        total0 += count[curCon][2];
    }
    getFinalRes(curCon - 1, path[curCon][curDiffPos][1]);

}

void printRes()
{
    int t, i, j;
    for(t = 0; t < 2; t++)
    {
        if(t == 0) cout<<total0;
        else if(t == 1) cout<<total1;

        for(i = 1; i <= conNum; i++)
        {
            int type = finalRes[t][i];
            for(j = 1; j <= count[i][0]; j++)
            {
                int color = con[i][j][1];
                if(color == type)
                    cout<<" "<<con[i][j][0];
            }
        }
        cout<<endl;
    }
}

int main()
{
    int i, j;
   
    //init();
    cin>>n;
    for(i = 1; i <= n; i++)
    {
        while(cin>>j && j != 0)
            input[i][j] = true;
    }
    getRever();
    if(dfsTag())
    {
        dpSolve();
        int minDiff = INT_MAX;
        int pos;
        for(i = 1; i <= dpR[conNum][0]; i++)
        {
            if(abs(dpR[conNum][i]) < minDiff)
            {
                minDiff = abs(dpR[conNum][i]); //FAINT,一直错在这儿,一开始没有加abs,一直WA
                pos = i;
            }
        }
        getFinalRes(conNum, pos);
        printRes();
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值