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; }