bzoj 2287 消失之物

2017成都联训Day2,身心疲惫~

据 dsfz JCY (%%% Orz) 大神说,此题可以用线段树分治解决,但我调了一晚上没调出来…(我太垃圾了…)

bzoj 2287 消失之物

Description

ftiasch 有 N 个物品, 体积分别是 W1,W2,,WN 。 由于她的疏忽, 第 i 个物品丢失了。 “要使用剩下的 N – 1 物品装满容积为 x 的背包,有几种方法呢?” — 这是经典的问题了。她把答案记为 Counti,x ,想要得到所有 1iN,1xM Counti,x 表格。

bzoj 2287 消失之物_第1张图片

Input

第1行:两个整数 N (1 ≤ N ≤ 2 × 103) 和 M (1 ≤ M ≤ 2 × 103),物品的数量和最大的容积。

第2行: N 个整数 W1, W2, …, WN, 物品的体积。

Output

一个 N × M 的矩阵, Count(i, x)的末位数字。

Sample Input

3 2
1 1 2

Sample Output

11
11
21

Hint

如果物品3丢失的话,只有一种方法装满容量是2的背包,即选择物品1和物品2。

来自黄学长的解题思路

首先,先考虑一个普普通通的背包DP。令 Fi,j 表示用前 i 个物品填满一个体积为 j 的背包的方案数,那么就有:

Fi,j=1: j=0Fi1,j:j>wiFi1,j+Fi1,jwi:0<jwi

用若干个物品填充0的体积,方案书只有一种,就是什么也不选;用前i个物品填充j的体积,当第i个物品的体积大于j时,i无论如何也不可能被使用,所以问题就等价于用前i-1个物品去填充j的体积。当 0<jwi 时,第i个物品要么用要么不用:如果用就要用剩下的i-1个物品填充其余的 jwi 的体积;如果第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,jCounti,jwi:jwi :在这种情况下物品i会被使用,不用物品i填充j的体积的方案数就等于用所有物品填充体积j的方案数再减去其中使用了物品i的方案数。强制使用使用物品i填充j的体积,就相当于禁止使用物品i填充了 jwi 的体积,(因为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;
}

你可能感兴趣的:(算法导论,C++语言)