多重背包 II —— 二进制优化
多重背包 II —— 二进制优化
核心思想:转换成0~1背包问题。———— 对个体拆分后全部打散,反正能够保证从全局上使得所有情况依然存在就可以了。
-
核心思想(前提建议先把多重背包朴素版算法搞清楚)
转换成0 ~ 1背包问题,对个体拆分后全部打散,反正能够保证从全局上使得所有情况依然存在就可以了。
-
举例分析(以下都使用该样例)
在选取第i种物品时,该物品有134件,二进制划分为**1,2,4,8,16,32,64,134 - (1 + 2 + 4 + 8 + 16 + 32 + 64) = 7 **这几个数字,这几个数字就相当于0 ~ 1背包问题中的每种背包。
仔细观察可知0 ~ 134内的任一数字均可由以上划分数字组合相加构成。这样即便打散后在宏观全局上仍然能够满足0~134的每种情况。
-
字母标准化
在选取第i种物品时,该物品有s件,二进制划分为1,2,4,8,16,······,2k-1,2k,s - (1 + 2 + ··· + 2k)这几个数字,这几个数字就相当于0 ~ 1背包问题中的每种背包。
仔细观察可知0 ~ s内的任一数字均可由以上划分数字组合相加构成。这样即便打散后在宏观全局上仍然能够满足0 ~ s的每种情况。
-
问题(只是几个帮助理解的小问题,上面已经理解的话可自行跳过)
-
问:在134被划分成1,2,4,8,16,32,64后已经可以组合出7 = 1+ 2 + 4了,为什么最后面还要搞个7?
答:现在已经转换成0 ~ 1背包来考虑问题了,那么前面1,2,4这几个数字已经被用来组合成1 + 2 + 4 + 8 + 16 + 32 + 64 = 127了,就已经不能再使用了。这时候要是想要再组合成134那么必然还要在127的基础上再加一个7。
-
问:0 ~ 127的数字都可以凑满了,134也可以凑出来了,那么128 ~ 133该怎么凑?
答:因为上面要凑134的时候又引入了一个数字7,并且0 ~ 127不需要这个7就可以轻松凑出来的,那么就有如下:
133 = 126 + 7;
132 = 125 + 7;
131 = 124 + 7;
········
········
-
-
代码示例:
#include<bits/stdc++.h> using namespace std; const int N = 11000; int n, m; int v[N], w[N], f[2010]; int main(){ scanf("%d%d", &n, &m); int cnt = 0; for(int i = 1; i <= n; i ++){ // v w s int a, b, s; scanf("%d%d%d", &a, &b, &s); int k = 1; while(k <= s){ cnt ++; v[cnt] = k * a; w[cnt] = k * b; s -= k; k *= 2; } if(s > 0){// s只能 >0 或者 =0 cnt ++; v[cnt] = s * a; w[cnt] = s * b; } } for(int i = 1; i <= cnt; i ++) for(int j = m; j >= v[i]; j --){ f[j] = max(f[j], f[j - v[i]] + w[i]); } printf("%d", f[m]); return 0; }