加载中…
个人资料
JosiahChiu
JosiahChiu
  • 博客等级:
  • 博客积分:0
  • 博客访问:7,096
  • 关注人气:87
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

POJ PKU 1112 Team Them Up 二分图染色 DP 模拟

(2011-06-28 22:02:20)
标签:

poj

pku

1112

team

them

up

二分图染色

dp

模拟

it

分类: 图论
题目描述:
n个人,告诉你每个人是否认识其他的人,让你把这n个人分成两组:
1. 每组至少一个人
2. 在同一组的人都互相认识
3. 这两组人数的差值最小

问:是否存在解
存在的话,输出分配方案。
解题报告:
首先,每组的所有人都要互相认识。
如果我们这样建无向图:连接i,j表示i和j不是互相认识,这样,如果ij之间没有边,就表示他们互相认识,那么,题目的要求就是分成两组,每组内部没有边(互相认识),即2分图。
这样,构图之后,染色,如果一边的两个节点颜色一样,就不是2分图,无解。

如果G是二分图,计算每个连通分支中分成属于x[i],y[i]2个组的人数(i表示连通分支);

下面是差值最小:
dp[j][k] = 1表示一组j人,另一组有k人可以成立。
那么如果枚举到第i个分支,则当前人数总和sum = x[0]+x[1] + .. + x[i] + y[0] + .. + y[i]

if (dp[j - x[i]][k - y[i]] == 1 && dp[j][k] == 0) dp[j][k] = 1;
if (dp[j - y[i]][k - x[i]] == 1 && dp[j][k] == 0) dp[j][k] = 1;

代码如下:
#include<iostream>
#include<vector>
using namespace std;
#define siz 101
int n, g[siz][siz], jeo[siz][siz], col[siz], gpnum;
int dp[siz][siz], path[siz][siz][2], pre[siz][siz][2];
vector<int> gp[siz * 2], ans[2];
void judge(int id, int co, int gpid)
{
    col[id] = co;
    gp[gpid * 2 + co].push_back(id);
    for(int i = 0; i < n; i++)
        if (jeo[id][i] && col[i] == -1)
            judge(i, co ^ 1, gpid);
}
void getans(int l, int r, int pos)
{
    if (l == 0 && r == 0) return;
    for(int i = 0; i < gp[path[l][r][pos]].size(); i++)
        ans[pos].push_back(gp[path[l][r][pos]][i]);
    getans(pre[l][r][0], pre[l][r][1], pos);
}
int main()
{
    while(scanf("%d", &n) != EOF)
    {
        memset(g, 0, sizeof(g));
        memset(jeo, 0, sizeof(jeo));
        memset(col, -1, sizeof(col));
        memset(dp, -1, sizeof(dp));memset(path, -1, sizeof(path));
        memset(pre, -1, sizeof(pre));
        ans[0].clear(); ans[1].clear();
        gpnum = 0;
        for(int i = 0; i < n + n; i++) gp[i].clear();
        for(int i = 0; i < n; i++)
        {
            int in; while(scanf("%d", &in) && in)
                g[i][in - 1] = 1;
        }
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                if (i != j && !(g[i][j] && g[j][i])) jeo[i][j] = jeo[j][i] = 1;
        for(int i = 0; i < n; i++)//染色
            if (col[i] == -1) judge(i, 0, gpnum++);
        bool flag = true;
        for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)
            if (jeo[i][j] && col[i] == col[j]) flag = false;
        if (!flag)
        {
            printf("No solution\n");
            continue;
        }
        int sum = 0; dp[0][0] = 1;
        for(int i = 0; i < gpnum; i++)
        {
            sum += gp[i * 2].size() + gp[i * 2 + 1].size();
            for(int j = sum, k = sum - j; j >= 0; j--)
                for(int t = 0; t < 2; t++)
                {
                    int A = i * 2 + t, B = i * 2 + (t ^ 1);
                    if (dp[j][k] == -1
                    && j - (int)gp[A].size() >= 0 && k - (int)gp[B].size() >= 0
                    && dp[j - gp[A].size()][k - gp[B].size()] == 1)
                    {
                        dp[j][k] = 1;
                        path[j][k][0] = A;
                        path[j][k][1] = B;
                        pre[j][k][0] = j - gp[A].size();
                        pre[j][k][1] = k - gp[B].size();
                    }
                }
        }
        int mmin = n, l, r;
        for(int i = 1; i < n; i++)
            if (i >= (n - i) && dp[i][n - i] == 1 && i + i - n < mmin)
            {

                mmin = i + i - n;
                l = i, r = n - i;
            }
        getans(l, r, 0); getans(l, r, 1);
        for(int i = 0; i < 2; i++)
        {
            printf("%d", ans[i].size());
            for(int j = 0; j < ans[i].size(); j++)
                printf(" %d", ans[i][j] + 1);
            printf("\n");
        }
    }
    return 0;
}

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有