1 0 1 2 0 0 1 0 0 0 1 1 0 0 0 0 0 0
Collection #1: Can't be divided. Collection #2: Can be divided.
初学DP,只知道01背包,没看过多重背包,只是感觉应该这样做,超时n次,网上看到别人的解法和分析证明收获颇多
http://www.cnblogs.com/walker01/archive/2010/02/06/1665033.html这个解法还是看不懂,待以后懂的更多了再来看
#include <cstdio> #include <cstring> #include <algorithm> #define LOCAL using namespace std; const int MAXN=60005; int m,maxv,n[9]; int dp[MAXN]; int mod[7]={1,42,42,14,84,210,42}; int main() { #ifdef LOCAL freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); #endif // LOCAL int kase=0; while(scanf("%d%d%d%d%d%d",&n[1],&n[2],&n[3],&n[4],&n[5],&n[6]),(n[1]||n[2]||n[3]||n[4]||n[5]||n[6])) { printf("Collection #%d:\n",++kase); //优化法一: http://poj.org/bbs?problem_id=1014 //POJ上给出的一种优化,不明白为什么 /* for(int i=1;i<=6;++i) if(n[i]>60) { if(1==(1&n[i])) n[i]=61; else n[i]=60; } */ //优化法二:自己苦想半天想出来的取模优化, // 看了 http://blog.csdn.net/gg_gogoing/article/details/38453273 // 才知道取模原来都是错的... // 同时又看到:取模前不为0,取模结果为0时,重新赋为模可避免问题(不知道能不能避免全部错误的发生) //1+2+3+4+5+6=21,则对i和21的最小偶公倍数取模,刚好能消去21*2n(n=0,1,2……),每一堆都能分得21*n for(int i=1;i<=6;++i) { bool flag=true; if(0==n[i]) flag=false; n[i]%=mod[i]; if(0==n[i]&&flag) n[i]=mod[i]; } int mini=n[1]; for(int i=2;i<=6;++i) if(n[i]<mini) mini=n[i]; if(1==(mini&1)) --mini; for(int i=1;i<=6;++i) n[i]-=mini; maxv=0; for(int i=1;i<=6;++i) maxv+=n[i]*i; if(1==(maxv&1)) { printf("Can't be divided.\n\n"); continue; } m=maxv/2; memset(dp,0,sizeof(dp)); for(int i=1;i<=6;--n[i]) { if(n[i]==0) { ++i; ++n[i]; continue; } for(int j=m;j>=i;--j) { dp[j]=max(dp[j-i]+i,dp[j]); } } if(m!=dp[m]) printf("Can't be divided.\n\n"); else printf("Can be divided.\n\n"); } return 0; }