0. 概念
将大问题划分成小问题来解决问题的一种技术,这种结束和分而治之的区别在于,分而治之划分成的子问题是彼此独立的,将子问题的方案组合来解决大问题。而动态规划划分出来的子问题是相互依赖的,比如斐波那契数列就是使用动态规划解决的。
1. 最少硬币找零
采取分而治之的方式实现功能。
将大问题划分成小问题,通过解决小问题,进而解决大问题。
- 创建缓存变量对象,保存小问题的解决方案
- 找到金额数是1时,找零的最佳方案。
- 找到金额数是2时,找零的最佳方案。
- 找到金额数是3时,找零的最佳方案。
- …
1.1 迭代
function minCoinChange(coins, amount) {
const cache = [];
const makeChange = (value) => {
if (!value) return [];
if (cache[value]) return cache[value];
// value的找零方案
let min = [];
// value - 1 的找零方案
let newMin;
for (let i = 0; i < coins.length; i++) {
const coin = coins[i];
const newAmount = value - coin;
if (newAmount >= 0) {
newMin = makeChange(newAmount);
}
if (
newAmount >= 0 &&
// 每个金额第一次for循环时,min=[]
(newMin.length < min.length - 1 || !min.length) &&
// 金额是1时,newMin=[],newAmount=0
(newMin.length || !newAmount)
) {
min = [coin].concat(newMin);
}
}
return cache[value] = min;
}
return makeChange(amount);
}
2 背包问题
.2.1 迭代
function snapStack(capacity, weights, values) {
const n = values.length;
const ks = [];
for (let i = 0; i <= n; i++) {
ks[i] = [];
}
for (let i = 0; i <= n; i++) {
for (let j = 0; j <= capacity; j++) {
if (i === 0 || j === 0) {
ks[i][j] = {
value: 0,
goods: []
};
}
else if (weights[i - 1] <= j) {
// 上一个物品在当前容量下的最优对象
const preDot = ks[i -1][j - weights[i - 1]];
const a = values[i - 1] + preDot.value;
const b = ks[i - 1][j].value;
ks[i][j] = a > b ? {value: a, goods: [...preDot.goods, i - 1]} : b;
}
else {
ks[i][j] = ks[i - 1][j];
}
}
}
return ks[n][capacity];
}
2.2 递归
function snapStackRecursive(capacity, weight, values) {
const cache = {};
const makeStack = (c) => {
if (c <= 0) return {value: 0, goods: []};
if (cache[c]) return cache[c];
let min = {value: 0, goods: []};
let newMin;
for (let i = 0; i < weight.length; i++) {
const w = weight[i];
const newAmount = c - w;
if (newAmount >= 0) {
newMin = makeStack(newAmount);
}
if (
newAmount >= 0 &&
(newMin.value <= min.value || !min.value) &&
(newMin.value > 0 || !newAmount) &&
newMin.goods.indexOf(i) < 0
) {
min = {value: newMin.value + values[i], goods: [...newMin.goods, w]}
}
}
return cache[c] = min;
}
return makeStack(capacity)
}
3. 最长公共子序列
3.1 迭代
function lcs(wordX, wordY) {
const x = wordX.length;
const y = wordY.length;
const l = [];
for (let i = 0; i <= x; i++) {
l[i] = [];
for (let j = 0; j <= y; j++) {
l[i][j] = {
len: 0,
chars: []
};
}
}
for (let i = 0; i <= x; i++) {
for (let j = 0; j <= y; j++) {
if (i === 0 || j === 0) {
l[i][j] = {
len: 0,
chars: []
};
}
else if (wordX[i - 1] === wordY[j - 1]) {
const prev = l[i - 1][j - 1];
l[i][j] = {
len: prev.len + 1,
chars: [...prev.chars, wordX[i - 1]]
}
}
else {
const a = l[i - 1][j];
const b = l[i][j - 1];
l[i][j] = a.len > b.len ? a : b;
}
}
}
return l[x][y];
}