http://acm.timus.ru/problem.aspx?space=1&num=1156

经典一维背包的原型是二维DP递推 由于其特殊性而被简化成了一维 就变成了背包

此题的特殊性导致无法简化成一维背包 所以用二维DP递推

思路:

先把数据分成m组 每组有两个集合 这两个集合只能任选其一

根据每个集合的组数和元素个数进行递推

代码及其注释:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#define LL long long

using namespace std;

const int N=150;
vector<int>hate[N];//根据不在一组而建立排斥关系
int f[N];//属于第几组 的 那个集合
int num1[N],num2[N];//第 i 组的集合 1 中元素个数 集合 2 中元素个数
int choose[N][N];//更新到第 i 组时 人数为 j 时是 选择了第 i 组的哪个集合
int sele[N];//第 i 组选择了哪个集合
bool dfs(int x,int k)
{
    if(f[x])
    {
        if(f[x]==k)
        return true;
        return false;//如果矛盾 则无解
    }
    f[x]=k;
    if(k>0) ++num1[k];
    else  ++num2[-k];
    for(unsigned int i=0;i<hate[x].size();++i)
    {
        if(!dfs(hate[x][i],-k))
        return false;
    }
    return true;
}
int main()
{
    //freopen("data.txt","r",stdin);
    int n,m;
    while(cin>>n>>m)
    {
        while(m--)
        {
            int i,j;
            cin>>i>>j;
            hate[i].push_back(j);
            hate[j].push_back(i);
        }
        memset(num1,0,sizeof(num1));
        memset(num1,0,sizeof(num2));
        memset(f,0,sizeof(f));
        int I=1;
        int l;
        for(l=1;l<=2*n;++l)
        {
            if(f[l]==0)
            {
                if(!dfs(l,I))
                break;
                ++I;
            }
        }
        if(l<=2*n)
        {printf("IMPOSSIBLE\n");continue;}//有矛盾的情况
        memset(choose,0,sizeof(choose));
        choose[0][0]=1;
        int m=I-1;
        for(int i=0;i<m;++i)
        {
            for(int j=0;j<=n;++j)
            {
                if(choose[i][j]==0)
                continue;
                choose[i+1][j+num1[i+1]]=1;
                choose[i+1][j+num2[i+1]]=-1;
            }
        }
        if(choose[m][n]==0)//无解
        {printf("IMPOSSIBLE\n");continue;}
         int k=n;
         for(int i=m;i>=1;--i)
         {
             sele[i]=choose[i][k];
             if(choose[i][k]>0)
             k=k-num1[i];
             else
             k=k-num2[i];
         }
         for(int i=1;i<=2*n;++i)
         {
             if((f[i]>0&&sele[f[i]]==1)||(f[i]<0&&sele[-f[i]]==-1))
             {
                printf("%d ",i);
             }
         }
         printf("\n");
         for(int i=1;i<=2*n;++i)
         {
             if(!(f[i]>0&&sele[f[i]]==1)&&!(f[i]<0&&sele[-f[i]]==-1))
             {
                printf("%d ",i);
             }
         }
         printf("\n");
    }
    return 0;
}

 

 

posted on 2012-10-23 20:10  夜->  阅读(330)  评论(0编辑  收藏  举报