poj 1112 二分图染色+dp

来源:互联网 发布:落樱神斧华盛顿知乎 编辑:程序博客网 时间:2024/04/30 03:22

题意:

给出n个人,和一些关系有向的关系,有向边a->b表示a认识b:
将n个人划分为两组,要求每组组内人必须互相都认识,同时要求两组人数差最小。

思路:

可以知道的是必须有a->b,且b->a的关系存在时,a和b才能被分为一组。考虑反向建图,若a,b不能分为一组,则连一条a,b之间的无向边。现在就变成了一系列连通块,对于他们去做二分图奇偶染色,将每个连通块根据奇偶划分为两部分,这两部分必然不能在一个集合,然后在这一系列的块中,每个连通块选一部分,形成总的集合a,剩下的点作为集合b,使用dp求取选择的最优值。若在此过程中,存在一个连通块不是二分图,那么一定无解。这个操作也可以通过带权并查集实现,维护每个连通块中的点到parent的关系,此关系其实也就是二分图中的奇偶关系。

代码:

代码略丑。。。

#include <cstdio>#include <vector>#include <cstring>using namespace std;const int size = 200;int n;int mp[size][size];bool vis[size];vector<int> con[size][2];int cnt;int dp[size][size];int st[size][size];int pr[size][size];int se[size];vector<int> at;bool dfs(int a,int so,int pre){    if(vis[a])     {        if(se[a] != so)            return false;        else            return true;    }    se[a] = so;    vis[a] = true;    con[cnt][so].push_back(a);    for(int i = 1; i <= n;i++)    {        if(pre==i || i == a) continue;        if(mp[i][a] == 0)        {            if(!dfs(i,1-so,a)) return false;        }    }    return true;}int main(){    int t;     scanf("%d",&t);    //t = 1;    while(t--)    {        cnt = 0;        for(int i = 0; i < size;i++)            for(int j = 0; j < 2; j++)                con[i][j].clear();        memset(mp,0,sizeof(mp));        scanf("%d",&n);        for(int i = 1; i <= n; i++)        {            int ms;            while(~scanf("%d",&ms)&&ms)                mp[i][ms] = 1;            mp[i][i] = 1;        }        for(int i = 1;i <= n; i++)        {            for(int j = 1;j <= n; j++)            {                if(mp[i][j] == 0 || mp[j][i] == 0)                    mp[i][j] = mp[j][i] = 0;                //printf(" %d",mp[i][j]);            }            //printf("\n");        }        memset(vis,false,sizeof(vis));        bool anss = true;        for(int i =1;i <= n; i++)            if(!vis[i])            {                anss = anss &&dfs(i,0,-1);                cnt++;            }        if(!anss)        {            printf("No solution\n");            if(t!=0)                printf("\n");            continue;        }        memset(dp,0,sizeof(dp));        dp[0][con[0][0].size()] = 1;        dp[0][con[0][1].size()] = 1;        st[0][con[0][0].size()] = 0;        st[0][con[0][1].size()] = 1;        for(int i = 1; i <cnt;i++)        {            int a = con[i][0].size();            int b = con[i][1].size();            for(int j = 0;j <= n;j++)            {                bool can = false;                if(j - a >= 0&&dp[i-1][j-a])                {                    can = true;                     pr[i][j] = j-a;                    st[i][j] = 0;                }                if(j - b >= 0 && dp[i-1][j-b])                {                    can = true;                    pr[i][j] = j-b;                    st[i][j] = 1;                }                if(can)                    dp[i][j] = 1;            }        }        int mis = 2e9;        int ans;        for(int j = 1; j < n; j++)        {            if(dp[cnt-1][j])            {                int dis = n - j -j;                if(dis < 0) dis = -dis;                if(dis <mis)                 {                    mis = dis;                    ans = j;                }            }        }        memset(vis,false,sizeof(vis));        at.clear();        for(int i = cnt-1; i >= 0; i--)        {            for(int j = 0; j < con[i][st[i][ans]].size();j++)            {                int mks = con[i][st[i][ans]][j];                at.push_back(mks);                vis[mks] = true;            }            ans = pr[i][ans];        }        printf("%d",n-at.size());        for(int i = 1;i <= n; i++)            if(!vis[i])printf(" %d",i);        printf("\n");        printf("%d",at.size());        for(int i = 0;i < at.size(); i++)            printf(" %d",at[i]);        printf("\n");        if(t!= 0)printf("\n");    }       return 0;}
0 0
原创粉丝点击