这么一道水题我还用各种麻烦的方法去做……这人没救了
看在这题做法挺多的份上,我就都写写好了……
1.CDQ分治
这个做法是我想到的……因为受到了Eden的新背包问题的启发……
定义solve(l,r)表示删除编号在[l,r]的物品并计算其DP数组,显然这个是可以折半往下递归的,用没删掉的那一半更新一下DP数组并带着DP数组递归下去即可。
T(n)=2T(n/2)+O(nm),解得T(n)=O(nmlogn),并且比O(nm)的做法慢不了多少。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=2010; 6 void CDQ(int,int,int); 7 int n,m,w[maxn],f[15][maxn]={{0}}; 8 int main(){ 9 scanf("%d%d",&n,&m); 10 for(int i=1;i<=n;i++)scanf("%d",&w[i]); 11 f[0][0]=1; 12 CDQ(1,n,0); 13 return 0; 14 } 15 void CDQ(int l,int r,int k){ 16 if(l==r){ 17 for(int i=1;i<=m;i++)printf("%d",f[k][i]); 18 printf("\n"); 19 return; 20 } 21 int mid=(l+r)>>1; 22 copy(f[k],f[k]+m+1,f[k+1]); 23 for(int i=mid+1;i<=r;i++)for(int j=m;j>=w[i];j--){ 24 f[k+1][j]+=f[k+1][j-w[i]]; 25 if(f[k+1][j]>=10)f[k+1][j]%=10; 26 } 27 CDQ(l,mid,k+1); 28 copy(f[k],f[k]+m+1,f[k+1]); 29 for(int i=l;i<=mid;i++)for(int j=m;j>=w[i];j--){ 30 f[k+1][j]+=f[k+1][j-w[i]]; 31 if(f[k+1][j]>=10)f[k+1][j]%=10; 32 } 33 CDQ(mid+1,r,k+1); 34 }