动态规划
很多[最优解]的问题都可以考虑使用动态规划解决
动态规划包括两个核心思想
- 状态转移方程
是指把一个大问题分解为多个重叠的子问题,寻找重叠子问题之间的关系
比如菲薄拉起数列的状态转移方案
如果表示斐波拉契数列的第n位的值
则有
在使用动态规划时,最苦难的就是找到状态转移方程
- db 缓存表
在求解的过程中,可能会遇到对相同的参数多次求解,为避免重复计算,可以使用缓存表将解值缓存下来
0-1背包问题
有一个背包,容积为c
给定一个物品列表: [o1,o2,o3,o4,...]
,每个物品都有两个属性,分别是价值和体积,例如{v:10,p:5}
问: 应该如何选择装入背包的物品,使得背包中的物品的总价值最大?
基础版
/***
*
* 获取0-1背包问题的最优解
* @param {Number} c背包体积
* @param {{v:Number,p:Number}[]} list 物品列表
* @returns {Number} 返回最大值
*
*
*/
function package(c, list) {
/**
* @param {Number} i
* @param {Number} rest;
*/
function _package(i, rest) {
// 如果不从当前的物品列表拿
if (i >= list.length) {
return 0;
}
const currPro = list[i];
if (currPro.v > rest) {
// 商品的体积大于剩余的空间了,商品一定不能拿,拿下一个
return _package(i + 1, rest)
} else {
// 当选择这件物品的时候
const selected = currPro.p + _package(i + 1, rest - currPro.v);
// 当不选这件物品的时候胡
const unSelected = _package(i + 1, rest);
return Math.max(selected, unSelected)
}
}
return _package(0, c)
}
const list = [
{ v: 10, p: 5 },
{ v: 7, p: 4 },
{ v: 11, p: 8 },
{ v: 9, p: 6 },
{ v: 2, p: 1 },
{ v: 17, p: 20 },
{ v: 25, p: 30 },
]
const c = 40;
const result = package(c, list);
console.log('result', result)
升级版
/**
*
* @param {Number} c 背包的体积
* @param {Array} rest 剩余商品
* @returns {{value:Number,choose:Number []}} max 最大值
*/
function package(c, rest) {
/**
*
* @param {Number} n 开始拿的位置
* @param {Number} rest 剩余空间
*/
function _package(n, rest) {
// 超出可以挑选商品的范围
if (n >= list.length) {
return {
value: 0,
choose: []
}
}
// 获取当前商品
const product = list[n];
// 当前商品的体积大于背包剩余的体积
if (product.v > rest) {
const next = _package(n + 1, rest);
return {
value: next.value,
choose: [0, ...next.choose]
}
} else {
// 选择当前物商品的价值
let next = _package(n + 1, rest - product.v);
let select = {
value: product.p + next.value,
choose: [1, ...next.choose]
}
// 不选择当前物品的机制
next = _package(n + 1, rest);
let unselect = {
value: next.value,
choose: [0, ...next.choose]
}
if (select.value > unselect.value) {
return select;
} else {
return unselect;
}
}
}
return _package(0, c)
}
const list = [
{ v: 10, p: 5 },
{ v: 7, p: 4 },
{ v: 11, p: 8 },
{ v: 9, p: 6 },
{ v: 2, p: 1 },
{ v: 17, p: 20 },
{ v: 25, p: 30 },
]
const c = 40;
const result = package(c, list);
console.log('result', result)
至尊版
/**
*
* @param {Number} c 背包的体积
* @param {Array} rest 剩余商品
* @returns {{value:Number,choose:Number []}} max 最大值
*/
function package(c, rest) {
// 缓存
const dp = {}
/**
*
* @param {Number} n 开始拿的位置
* @param {Number} rest 剩余空间
*/
function _package(n, rest) {
// 超出可以挑选商品的范围
if (n >= list.length) {
return {
value: 0,
choose: []
}
}
const key = `${n}-${rest}`;
if (dp[key]) {
return dp[key];
}
// 获取当前商品
const product = list[n];
// 当前商品的体积大于背包剩余的体积
if (product.v > rest) {
const next = _package(n + 1, rest);
dp[key] = {
value: next.value,
choose: [0, ...next.choose]
}
} else {
// 选择当前物商品的价值
let next = _package(n + 1, rest - product.v);
let select = {
value: product.p + next.value,
choose: [1, ...next.choose]
}
// 不选择当前物品的机制
next = _package(n + 1, rest);
let unselect = {
value: next.value,
choose: [0, ...next.choose]
}
if (select.value > unselect.value) {
dp[key] = select;
} else {
dp[key] = unselect;
}
}
return dp[key];
}
const result = _package(0, c);
console.log('dp', dp)
return result;
}
const list = [
{ v: 10, p: 5 },
{ v: 7, p: 4 },
{ v: 11, p: 8 },
{ v: 9, p: 6 },
{ v: 2, p: 1 },
{ v: 17, p: 20 },
{ v: 25, p: 30 },
]
const c = 40;
const result = package(c, list);
console.log('result', result)