动态规划
很多[最优解]的问题都可以考虑使用动态规划解决
动态规划包括两个核心思想
- 状态转移方程
是指把一个大问题分解为多个重叠的子问题,寻找重叠子问题之间的关系
比如菲薄拉起数列的状态转移方案
如果表示斐波拉契数列的第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)
