1156. Two Rounds

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;

}

 

 

你可能感兴趣的:(round)