動態規劃是一種將復雜問題分解成更小的相互依賴的子問題來解決的優化技術
關鍵點:創建dp數組
1. 最小路徑和
//給定一個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和為最小。
// 說明:每次只能向下或者向右移動一步。
// 示例:
// 輸入:
// [
// [1,3,1],
// [1,5,1],
// [4,2,1]
// ]
// 輸出: 7
// 解釋: 因為路徑 1→3→1→1→1 的總和最小。
//難度:中等
//解題思想:拆分成小問題算出到達每個子位置的最小路徑和
var minPathSum = function (grid) {
var m = grid.length;
var n = grid[0].length;
var dp = Array.from({ length: grid.length }, item => item = []);
for (var i = 0; i < m; i++) {
for (var j = 0; j < n; j++) {
if (i === 0 && j === 0) {
dp[i][j] = grid[i][j]
} else if (i === 0 && j !== 0) {
dp[i][j] = grid[i][j] + dp[i][j - 1]
} else if (i !== 0 && j === 0) {
dp[i][j] = grid[i][j] + dp[i - 1][j]
} else {
dp[i][j] = Math.min(grid[i][j] + dp[i][j - 1], grid[i][j] + dp[i - 1][j])
}
}
}
return dp[m - 1][n - 1]
};
2.打家劫舍
// 你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。
// 給定一個代表每個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。
// 示例 1:
// 輸入: [1,2,3,1]
// 輸出: 4
// 解釋: 偷竊 1 號房屋 (金額 = 1) ,然后偷竊 3 號房屋 (金額 = 3)。
// 偷竊到的最高金額 = 1 + 3 = 4 。
//難度:中等
//解題思想: 根據3次以內的情況得到計算方程
var rob = function (nums) {
if (nums.length === 0) return 0;
//dp dynamic programming 動態規劃簡寫
var dp = [];
dp[0] = 0;
dp[1] = nums[0];
for (var i = 2; i <= nums.length; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]);
}
return dp.pop();
};
3.乘積最大子序列
// 給定一個整數數組 nums ,找出一個序列中乘積最大的連續子序列(該序列至少包含一個數)。
// 示例 1:
// 輸入: [2,3,-2,4]
// 輸出: 6
// 解釋: 子數組 [2,3] 有最大乘積 6。
// 示例 2:
// 輸入: [-2,0,-1]
// 輸出: 0
// 解釋: 結果不能為 2, 因為 [-2,-1] 不是子數組。
var maxProduct = function (nums) {
if (nums.length === 0) return 0;
//1.設置初始默認最大值,及默認乘積最大值,因為涉及負數最小值,當遇到負數時,最大和最小會互換,所以設置保存最小乘積變量.
let max = nums[0], imax = 1, imin = 1;
for (let i = 0; i < nums.length; i++) {
if (nums[i] < 0) [imax, imin] = [imin, imax];
imax = Math.max(imax * nums[i], nums[i]);
imin = Math.min(imin * nums[i], nums[i]);
max = max > imax ? max : imax;
}
return max;
};
4. 最少硬幣找零
//給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。
// 示例 1:
// 輸入: coins = [1, 2, 5], amount = 11
// 輸出: 3
// 解釋: 11 = 5 + 5 + 1
// 示例 2:
// 輸入: coins = [2], amount = 3
// 輸出: -1
//解題思路:所要金額可用amount減去coins中任一面值+1得到
//例如amount = 15,coins=[1,2,11,15],可由f(amount-coin)+1得到,即f(0)+1,f(4)+1,f(13)+1,f(14)+1
var coinsChange = function(amount,coins){
let dp = Array.from({length: amount+1},item=>item=Infinity);
dp[0] = 0;
for(var i=1;i<=amount;i++){
for(var coin of coins){
console.log(dp)
if(i-coin>=0){
//i=1時,dp[1]=dp[0]+1=1
dp[i] = Math.min(dp[i],dp[i-coin]+1)
}
}
}
return dp[amount]===Infinity?-1:dp[amount]
}
console.log(coinsChange(16,[1,2,5]))
5. 最長公共子序列
// 給定兩個字符串 text1 和 text2,返回這兩個字符串的最長公共子序列。
// 一個字符串的 子序列 是指這樣一個新的字符串:它是由原字符串在不改變字符的相對順序的情況下刪除某些字符(也可以不刪除任何字符)后組成的新字符串。
// 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。兩個字符串的「公共子序列」是這兩個字符串所共同擁有的子序列。
// 若這兩個字符串沒有公共子序列,則返回 0。
// 示例 1:
// 輸入:text1 = "abcde", text2 = "ace"
// 輸出:3
// 解釋:最長公共子序列是 "ace",它的長度為 3。
// 示例 2:
// 輸入:text1 = "abc", text2 = "abc"
// 輸出:3
// 解釋:最長公共子序列是 "abc",它的長度為 3。
// 示例 3:
// 輸入:text1 = "abc", text2 = "def"
// 輸出:0
// 解釋:兩個字符串沒有公共子序列,返回 0
解題思路:列出一個二維dy數組,列出每個位置子序列長度
var longestCommonSubsequence = function (text1, text2) {
var m = text1.length,//rows
n = text2.length, //cols
dp = Array.from({ length: n + 1 }, item => item = new Array(m + 1).fill(-Infinity));
dp[0][0] = 0;
for (var i = 0; i <= n; i++) {
for (var j = 0; j <= m; j++) {
if (i === 0 || j === 0) {//有一方為0結論為0
dp[i][j] = 0
} else if (text1[j-1] === text2[i-1]) {//i,j對應字符相等
dp[i][j] = dp[i - 1][j - 1] + 1
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[n][m];
};