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;
}
浙公网安备 33010602011771号