题目大意
有$N$个物品,体积分别是$W_1$,$W_2$,…,$W_N$,第$i$个物品丢失了。要使用剩下的$N$-$1$物品装满容积为$x$的背包,有几种方法呢?。把答案记为Count($i$,$x$),输出$1\le i\le N,1\le x\le M$的Count($i$,$x$)表格。
题目分析
这道题是有$O(nm)$的做法的,但是此处不做解释,可以参考PopoQQQ或hzwer的博客。
我们谈一种普适性更强,思维要求更低的方法:CDQ分治。
什么?CDQ分治不是解决三维偏序问题的吗?
我们仔细想一想CDQ分治的本质,其本质就是处理左边对右边的影响,反过来也可以处理右边对左边的影响。那合起来呢,不就仅仅缺了自己的影响?这不就是少一个物品的$Dp$吗?
我们将这种方法称为左右逼近。
我们在递归左区间前处理右边对左边的影响,然后清楚影响,再递归右区间前处理左边对右边的影响,到达每个$L==R$的地方即为答案。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<algorithm> #include<iostream> #include<iomanip> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> #include<queue> using namespace std; inline const int Get_Int() { int num=0,bj=1; char x=getchar(); while(x<'0'||x>'9') { if(x=='-')bj=-1; x=getchar(); } while(x>='0'&&x<='9') { num=num*10+x-'0'; x=getchar(); } return num*bj; } int n,m,f[2005],a[2005]; void CDQBinary(int Left,int Right) { if(Left==Right) { for(int i=1; i<=m; i++)printf("%d",f[i]); putchar('\n'); return; } int tmp[2005],mid=(Left+Right)>>1; memcpy(tmp,f,sizeof(f)); for(int i=mid+1; i<=Right; i++) for(int j=m; j>=a[i]; j--) f[j]=(f[j]+f[j-a[i]])%10; CDQBinary(Left,mid); memcpy(f,tmp,sizeof(f)); for(int i=Left; i<=mid; i++) for(int j=m; j>=a[i]; j--) f[j]=(f[j]+f[j-a[i]])%10; CDQBinary(mid+1,Right); } int main() { n=Get_Int(); m=Get_Int(); f[0]=1; for(int i=1; i<=n; i++)a[i]=Get_Int(); CDQBinary(1,n); return 0; }
|