2017成都联训Day2,身心疲惫~
据 dsfz JCY (%%% Orz) 大神说,此题可以用线段树分治解决,但我调了一晚上没调出来…(我太垃圾了…)
ftiasch 有 N 个物品, 体积分别是 W1,W2,…,WN 。 由于她的疏忽, 第 i 个物品丢失了。 “要使用剩下的 N – 1 物品装满容积为 x 的背包,有几种方法呢?” — 这是经典的问题了。她把答案记为 Counti,x ,想要得到所有 1≤i≤N,1≤x≤M 的 Counti,x 表格。
第1行:两个整数 N (1 ≤ N ≤ 2 × 103) 和 M (1 ≤ M ≤ 2 × 103),物品的数量和最大的容积。
第2行: N 个整数 W1, W2, …, WN, 物品的体积。
一个 N × M 的矩阵, Count(i, x)的末位数字。
3 2
1 1 2
11
11
21
如果物品3丢失的话,只有一种方法装满容量是2的背包,即选择物品1和物品2。
首先,先考虑一个普普通通的背包DP。令 Fi,j 表示用前 i 个物品填满一个体积为 j 的背包的方案数,那么就有:
用若干个物品填充0的体积,方案书只有一种,就是什么也不选;用前i个物品填充j的体积,当第i个物品的体积大于j时,i无论如何也不可能被使用,所以问题就等价于用前i-1个物品去填充j的体积。当 0<j≤wi 时,第i个物品要么用要么不用:如果用就要用剩下的i-1个物品填充其余的 j−wi 的体积;如果第i个物品不用,那么问题就等价于用前i-1个物品填充j的体积(再根据加法原理,可得该式)。
再考虑 Counti,j :使用除了物品i之外的n-1个物品,填充j的体积的方案数。
Counti,j=fn,j:0<j<wi :当物品i的体积大于j时,i本来就不可能被用到,自然与 fn,j 相等。
Counti,j=fn,j−Counti,j−wi:j≥wi :在这种情况下物品i会被使用,不用物品i填充j的体积的方案数就等于用所有物品填充体积j的方案数再减去其中使用了物品i的方案数。强制使用使用物品i填充j的体积,就相当于禁止使用物品i填充了 j−wi 的体积,(因为i占据了j中 wi 的体积)。
这样的话我们就可以递推了,时间复杂度 O(n2) 。
我没有BZOJ权限号,此代码未经过任何测试(除了过了样例)。
#include
#include
#include
using namespace std;
const int maxn=2000+10;
int w[maxn],f[maxn],c[maxn][maxn];
int main(){
int n,m;scanf("%d%d",&n,&m);f[0]=1;
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=1;i<=n;i++){
for(int j=m;j>=w[i];j--){
f[j]=(f[j]+f[j-w[i]])%10;
}
}
for(int i=1;i<=n;i++){
c[i][0]=1;
for(int j=1;j<=m;j++){
if(j-w[i]>=0){
c[i][j]=(f[j]-c[i][j-w[i]]+10)%10;
}else c[i][j]=f[j];
printf("%d",c[i][j]);
}
printf("\n");
}
return 0;
}