<—点个赞吧QwQ
宣传一下算法基础课整理
有 $N$ 种物品和一个容量是 $V$ 的背包。
第 $i$ 种物品最多有 $s_i$ 件,每件体积是 $v_i$,价值是 $w_i$。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,$N,V$,用空格隔开,分别表示物品种数和背包容积。
接下来有 $N$ 行,每行三个整数 $v_i, w_i, s_i$,用空格隔开,分别表示第 $i$ 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
$0 \lt N \le 1000$
$0 \lt V \le 2000$
$0 \lt v_i, w_i, s_i \le 2000$
提示:
本题考查多重背包的二进制优化方法。
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
思路
这道题是二进制差分的方法来优化的多重背包问题。
这里讲一下,为什么可以那样拆分:
比如说$s=127$,分成几个数,按照分的思路分出来就是$1,2,4,8,16,32,64$,大家可以试一下,这里的几个数,可以凑出所有$0\le k\le 127$的所有数,而背包问题会选择最优解。
最后按$0/1$背包问题的思路进行枚举即可。
代码
#include <iostream>
using namespace std;
const int N = 2010;
int n,m;
int f[N];
int main () {
cin >> n >> m;
for (int i = 1;i <= n;i++) {
int w,v,s;
cin >> w >> v >> s;
for (int k = 1;k <= s;k <<= 1) {
for (int j = m;j >= w * k;j--) f[j] = max (f[j],f[j - w * k] + v * k);
s -= k;
}
if (s) {
for (int j = m;j >= w * s;j--) f[j] = max (f[j],f[j - w * s] + v * s);
}
}
cout << f[m] << endl;
return 0;
}
寄了,二进制优化突然想不懂了,教教
/bx/bx/bx/bx/bx/bx/bx/bx/bx
if (s) {
for (int j = m;j >= w * s;j–) f[j] = max (f[j],f[j - w * s] + v * s);
} 这个为啥可以直接这样算 而不是 while(s–)
for(int j=m;j>=w;j–)
f[j]=max(f[j],f[j-w]+v);
建议再去看看二进制拆分的视频,我这里给一个比较差的解释:
$1,2,4,8,16…2^n,k$ 其中 $k$ 是剩余的。
由于 $k\le 2^n$ 所以 $k$ 一定可以由 $2^x,(x\le n)$ 的和组成,所以直接用 $k$ 倍就好了。
仔细来说,就是 $[0,m-k]$ 的范围 $f$ 数组既会吸收原来的答案,又会更新加上 $k$ 的答案,所以范围扩展到 $[0,m-k+k]=[0,m]$
好的,十分感谢大佬的耐心解答
写的好简洁,已赞!
定义了一个cnt,是有什么用呀🥺🥺
忘删了hh