DP分析法
01背包
背包中的所有物品都只有一个
背包体积V 物品数N 每个物品的价值Wi 体积Vi
- 集合:只从前i个物品里选,且总体积不大于j的所有选法
- 集合划分
代码
var readline = require('readline');
rl = readline.createInterface({
input:process.stdin,
output:process.stdout
})
var items=[];
let n=-1;//数据行数 物品数
let m=-1;//背包总体积
rl.on('line',function(data){
//第一行数据为 n,m 其余是物品数据
if(n===-1){
let firstRow = data.split(" ");
n=parseInt(firstRow[0]);
m=parseInt(firstRow[1]);
}else{
items.push(data);
}
if(items.length === n){
//数据全部处理完,开始写逻辑
const v = new Int32Array(n+1);
const w = new Int32Array(n+1);
for(let i =1;i<=n;i++){
let item = items[i-1].split(" ");
v[i]=parseInt(item[0]);
w[i]=parseInt(item[1]);
}
let res = zeroOneBag(n,m,v,w);
console.log(res);
}
})
function zeroOneBag(n,m,v,w){
const f = [];
for(let i=0;i<=n;i++){
f[i]=new Int32Array(m+1);
}
for(let i=1;i<=n;i++){
for(let j=0;j<=m;j++){
f[i][j]=f[i-1][j]
if(v[i]<=j){
f[i][j]=Math.max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
}
}
return f[n][m];
}
优化
f[i][j] = Max(f[i-1][j],f[i-1][j-v[i]]+w[i])
只与第i-1层有关,那么就可以利用滚动数组压缩为一维
function zeroOneBag(n,m,v,w){
const f = new Int32Array(m+1);
for(let i=1;i<=n;i++){
for(let j=m;j>=0;j--){
if(v[i]<=j){
f[j]=Math.max(f[j],f[j-v[i]]+w[i]);
}
}
}
return f[m];
}
//要保证 f[j]是第i-1层算过的 而f[j-v[i]]+w[i]是第i层算的 j就要从大到小遍历
完全背包
背包中所有物品都有无数个
背包体积V 物品数N 每个物品的价值Wi 体积Vi
- 集合 只选前i种物品且总体积小于j的所有选法
- 集合的划分
朴素的做法就是比01背包多加一重循环,遍历第i种物品的数量0-k
但是三重循环时间复杂度太高了
for(let i=1;i<=n;i++){
for(let j=0;j<=m;j++){
f[i][j]=f[i-1][j];
for(let k=0;k*v[i]<=j;k++){
f[i][j]=Math.max(f[i][j],f[i-1][j-k*v[i]]+w[i])
}
}
}
优化
f[i][j] = Max( f[i-1][j],f[i-1][j-v]+w],f[i-1][j-2v]+2w,...,f[i-1][j-kv]+kw) ①
f[i][j-v]=Max(f[i-1][j-v],f[i-1][j-2v]+w ,f[i-1][j-3v]+2w,...,f[i-1][j-kv]+(k-1)w) ②
①②错位相减=>f[i][j] = Max( f[i-1][j], f[i][j-v]+w )
=> 只跟第i层、第i-1层有关 =>一维数组
function fullBag(n,m,v,w){
const f= new Int32Array(m+1);
for(let i=1;i<=n;i++){
for(let j=0;j<=m;j++){
if(v[i]<=j){
f[j]=Math.max(f[j],f[j-v[i]]+w[i]);
}
}
}
return f[m];
}
多重背包
背包中第i种物品有s[i]个
背包体积V 物品数N 每个物品的价值Wi 体积Vi
- 集合的划分
思路和完全背包的朴素做法差不多,就是k值增加了一个上限
s[i]
代码
function multiBag(n,m,v,w,s){
const f=[];
for(let i=0;i<=n;i++){
f[i]=new Int32Array(m+1);
}
for(let i=1;i<=n;i++){
for(let j=0;j<=m;j++){
f[i][j]=f[i-1][j];
for(let k=0;k<=s[i]&&k*v[i]<=j;k++){
f[i][j]=Math.max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
}
}
return f[n][m];
}
优化:二进制优化
二进制思想: n=100可以由2^i(i=0-n)组合表示:1+2+4+8+16+32...(n减完之前的数之后不足 2^i,取最后剩余的数),相当于把十进制的数用二进制来表示。那么要判断的数据就从100降低到100的二进制的位数。
对于多重背包问题来说,就从一种多个物品,转化成多种单个物品,就可以用0-1背包的方法解。
var readline = require('readline');
rl=readline.createInterface({
input:process.stdin,
output:process.stdout
});
let inputs = [];
let n = -1;
let m = -1;
rl.on('line',function(data){
if(n===-1){
const line = data.split(" ");
n=parseInt(line[0]);
m=parseInt(line[1]);
}else{
inputs.push(data)
}
if(inputs.length === n){
const v=[];
const w=[];
let cnt=0;
for(let i=1;i<=n;i++){
const input = inputs[i-1].split(" ");
let pv=parseInt(input[0]);
let pw=parseInt(input[1]);
let s=parseInt(input[2]);
let k=1;
while(k<=s){
cnt++;
v[cnt]=pv*k;
w[cnt]=pw*k;
s-=k;
k*=2;
}
if(s>0){
cnt++;
v[cnt]=pv*s;
w[cnt]=pw*s;
}
}
const res = multiBag(cnt,m,v,w);
console.log(res);
}
})
function multiBag(n,m,v,w){//其实就是01背包了
const f=new Int32Array(m+1);
for(let i=1;i<=n;i++){
for(let j=m;j>=v[i];j--){
f[j]=Math.max(f[j],f[j-v[i]]+w[i]);
}
}
return f[m];
}
分组背包
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j是组内编号。
- 集合的划分
和多重背包问题比较相似,不过没有什么好的优化方法。
let fs = require('fs');
let buf = '';
process.stdin.on('readable', function() {
let chunk = process.stdin.read();
if (chunk) buf += chunk.toString();
});
process.stdin.on('end', function() {
const lines=buf.split('\n').map(x=>x.split(" ").map(x=>parseInt(x)));
const firstLine = lines.shift();
const n=firstLine[0];
const m=firstLine[1];
let s=[];
let v=[];
let w=[];
for(let i=1;i<=n;i++){
s[i]=lines.shift()[0];
v[i]=new Int32Array(s[i]+1);
w[i]=new Int32Array(s[i]+1);
for(let j=1;j<=s[i];j++){
let item = lines.shift();
v[i][j]=item[0];
w[i][j]=item[1];
}
}
const res = groupBag(n,m,s,v,w);
console.log(res);
});
function groupBag(n,m,s,v,w){
let f=[];
for(let i=0;i<=n;i++){
f[i]=new Int32Array(m+1);
}
for(let i=1;i<=n;i++){
for(let j=0;j<=m;j++){
f[i][j]=f[i-1][j];
for(let k=1;k<=s[i];k++){
if(v[i][k]<=j){
f[i][j]=Math.max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
}
}
}
}
return f[n][m];
}