ARC073B Simple Knapsack_动态规划 || DP



upd - 2021.8.8 : 修正笔误。

upd - 2021.8.9 : 修正格式。

这题其实没有想象中的那么复杂。

楼上两位的题解都有一点麻烦了。

所以提供一种简单易懂的方法。



这道题实质上就是个01背包。

但是一看数据范围:\(W \leq 10^9\) , \(w_i \leq 10^9\)。

\(10^9\) 的01背包您开心么?

很明显普通的01背包是不可以的。

注意到题面里有这样一句话:

For each \(i =2,3…,N\),\(w_1\leq w_i \leq w_1+3\)

那我们考虑如何“缩小”这个数据范围。

既然 \(w_i\) 是一定大于等于 \(w_1\) 的。

我们干脆就直接给 所有 \(w_i\) 减去一个 \(w_1-1\)。

这里为了避免 \(w_1=0\) 所以是 \(w_1-1\)。

然后我们的背包容积也会改变。

这时候对于每个物品的最大容积就是 \(\sum^n_{i=1} (w[i]-(w[1]-1))\)。

设 \(f[item][size]\) 表示用 \(item\) 个物品,用了 \(size\) 空间的最大价值。

那么我们考虑转移。

因为这里背包真实的容积还是 \(m\) 。

所以我们在决策的时候要看这个状态在原来的情况下是否合法。

所以考虑之前用了 \(j\) 个东西,现在对于物品 \(i\) 用了 \(k\) 的空间。

当前的状态如果有效。

那么就需要满足 \(j\times(w[1]-1)+k \leq m\)。

就相当于你现在对于那个物品所用的空间,加上之前所有减去的空间,能够在原来的背包里面放下。

因为我们的 \(j\) 是不确定的。

所以要多开一层循环来枚举。

这部分的代码如下:

    for(register int i=1;i<=n;++i){
for(register int j=i;j>=1;--j){//倒序枚举是为了后面的转移
for(register int k=sum;k>=w[i];--k){//sum 是我们说的对于每个物品的最大容积。
f[j][k]=max(f[j][k],f[j-1][k-w[i]]+v[i]);
if(j*(w[1]-1)+k<=m){
res=max(f[j][k],res);
} //状态合法。
}
}
}


可能会有点绕。

你可以把它理解成对于每个物品都进行一次 01背包的形式的决策和转移。

那么到这里,这道题就算完结了。

不过还要记得开 ​​long long​​(在状态是否合法那里还有一些地方都会爆 ​​int​​ )

完整代码可以看:​​https://www.luogu.com.cn/paste/ajoe2gr0​


 ​