二、完全背包问题
物品使用次数没有限制
例题 Acwing3
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 ii 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10
题解 朴素解法
const N = 1010;
let v = new Int32Array(N);
let w = new Int32Array(N);
let f = [];
let init = n => {
for (let i = 0; i <= n; i++) {
f[i] = new Int32Array(N);
}
}
let n = 0;
let buf = '';
process.stdin.on('readable', function () {
let chunk = process.stdin.read();
if (chunk) buf += chunk.toString();
});
let getInputNums = line => line.split(' ').filter(s => s !== '').map(x => parseInt(x));
let getInputStr = line => line.split(' ').filter(s => s !== '');
process.stdin.on('end', function () {
buf.split('\n').forEach(function (line, lineIdx) {
if (lineIdx === 0) {
n = getInputNums(line)[0];
m = getInputNums(line)[1];
init(n);
} else if (lineIdx <= n) {
let a = getInputNums(line)[0];
let b = getInputNums(line)[1];
v[lineIdx] = a;
w[lineIdx] = b;
if (lineIdx === n) {
for (let i = 1; i <= n; i++) { //件数
for (let j = 0; j <= m; j++) { //体积
for (let k = 0; k * v[i] <= j; k++) 物品i使用k次
f[i][j] = Math.max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
}
}
console.log(f[n][m]);
}
}
});
});
优化 找规律如下
f[i,j] = max(f[i-1,j], f[i,j-v]+w); //当j>v[i]时
优化版
const N = 1010;
let v = new Int32Array(N);
let w = new Int32Array(N);
let f = [];
let init = n => {
for (let i = 0; i <= n; i++) {
f[i] = new Int32Array(N);
}
}
let n = 0;
let buf = '';
process.stdin.on('readable', function () {
let chunk = process.stdin.read();
if (chunk) buf += chunk.toString();
});
let getInputNums = line => line.split(' ').filter(s => s !== '').map(x => parseInt(x));
let getInputStr = line => line.split(' ').filter(s => s !== '');
process.stdin.on('end', function () {
buf.split('\n').forEach(function (line, lineIdx) {
if (lineIdx === 0) {
n = getInputNums(line)[0];
m = getInputNums(line)[1];
init(n);
} else if (lineIdx <= n) {
let a = getInputNums(line)[0];
let b = getInputNums(line)[1];
v[lineIdx] = a;
w[lineIdx] = b;
if (lineIdx === n) {
for (let i = 1; i <= n; i++) { //件数
for (let j = 0; j <= m; j++) { //体积
f[i][j] = f[i - 1][j];
if (j >= v[i]) f[i][j] = Math.max(f[i][j], f[i][j - v[i]] + w[i]);
}
}
console.log(f[n][m]);
}
}
});
});
再优化 二维 -> 一维
const N = 1010;
let v = new Int32Array(N);
let w = new Int32Array(N);
let f = new Int32Array(N);
let n = 0;
let buf = '';
process.stdin.on('readable', function () {
let chunk = process.stdin.read();
if (chunk) buf += chunk.toString();
});
let getInputNums = line => line.split(' ').filter(s => s !== '').map(x => parseInt(x));
let getInputStr = line => line.split(' ').filter(s => s !== '');
process.stdin.on('end', function () {
buf.split('\n').forEach(function (line, lineIdx) {
if (lineIdx === 0) {
n = getInputNums(line)[0];
m = getInputNums(line)[1];
} else if (lineIdx <= n) {
let a = getInputNums(line)[0];
let b = getInputNums(line)[1];
v[lineIdx] = a;
w[lineIdx] = b;
if (lineIdx === n) {
//与01背包不同,j循环不用倒序,因为完全背包的f[j - v[i]]就是第i层的,完整为f[i][j-v[i]] + w[i]
for (let i = 1; i <= n; i++) { //件数
for (let j = v[i]; j <= m; j++) { //体积
f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
}
}
console.log(f[m]);
}
}
});
});
注意
f[i,j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i]);//01背包
f[i,j] = max(f[i-1][j], f[ i ][j-v[i]]+w[i]) //完全背包
区别很小,所以最终版j层循环有区别(一正序、一倒序)。