1. 程式人生 > >LeetCode兩個爬樓梯題目解析(動態規劃)

LeetCode兩個爬樓梯題目解析(動態規劃)

原題:

就光貼原始碼好了,解析都在註釋裡面了。

import java.util.HashMap;

/**
 * leetCode 爬樓梯  動態規劃
 * <p>
 * 1. 簡易爬樓梯,總的有X級臺階 每一次可以上 一級 或者 兩級 計算上樓梯的方法總數
 * 假設你正在爬樓梯。需要 n 階你才能到達樓頂。
 * <p>
 * 每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
 * <p>
 * 解題看前三個
 * <p>
 * 2. 高階爬樓梯,
 * 陣列的每個索引做為一個階梯,第 i個階梯對應著一個非負數的體力花費值 cost[i](索引從0開始)。
 * <p>
 * 每當你爬上一個階梯你都要花費對應的體力花費值,然後你可以選擇繼續爬一個階梯或者爬兩個階梯。
 * <p>
 * 您需要找到達到樓層頂部的最低花費。在開始時,你可以選擇從索引為 0 或 1 的元素作為初始階梯。
 * <p>
 * 示例 1:
 * <p>
 * 輸入: cost = [10, 15, 20]
 * 輸出: 15
 * 解釋: 最低花費是從cost[1]開始,然後走兩步即可到階梯頂,一共花費15。
 * 示例 2:
 * <p>
 * 輸入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
 * 輸出: 6
 * 解釋: 最低花費方式是從cost[0]開始,逐個經過那些1,跳過cost[3],一共花費6。
 * 注意:
 * <p>
 * cost 的長度將會在 [2, 1000]。
 * 每一個 cost[i] 將會是一個Integer型別,範圍為 [0, 999]。
 *
 **/
public class Stairs { public static void main(String[] args) { long timeStart = System.currentTimeMillis(); // System.out.println(climbingStairs(43)); long timeEnd = System.currentTimeMillis(); // System.out.println(timeEnd - timeStart); // timeStart = System.currentTimeMillis();
// System.out.println(climbingStairsWithMap(43)); // timeEnd = System.currentTimeMillis(); // System.out.println(timeEnd - timeStart); // timeStart = System.currentTimeMillis(); // System.out.println(climbingStairsFib(43)); // timeEnd = System.currentTimeMillis(); // System.out.println(timeEnd - timeStart);
int[] costs = {1, 100, 1, 1, 1, 100, 1, 1, 100, 1}; // int [] costs = {10,15,20}; System.out.println(minCostClimbingStairs(costs)); timeEnd = System.currentTimeMillis(); System.out.println(timeEnd - timeStart); } // 第一感覺就是正向推,然後計算各種不一樣的排列組合的結果相加。然後枚舉出每一種可能然後相加,很明顯不對 假如100級臺階, // 發過來考慮,每一次上臺階都是1 和 2 的可能,那麼當上到了最後一次 也是 1 和 2 的可能。1 則是最後只剩下一個臺階 2 的話就是 最後剩下兩個臺階, // 這樣的話 假設現在的可能性是 F(n) 那麼應該是等於 F(n-1) 對應最後走一個臺階走完 與 F(n-2) 最後一次走兩個臺階 的和, // 那麼就有了F(n) = F(n-1) + F(n-2); // 已經可以確定 F(1) = 1 , F(2) = 2 那麼所有的可能一層層遞迴最後都將化簡為 F(1) 和 F(2) // public static int climbingStairs(int stairsNum) { if (stairsNum <= 0) { return 0; } else if (stairsNum == 1) { return 1; } else if (stairsNum == 2) { return 2; } return climbingStairs(stairsNum - 1) + climbingStairs(stairsNum - 2); } // 簡單計算,小的資料沒啥問題,但是提交就錯了,計算超時,原因是遞迴呼叫後相同的臺階數計算太多次,比如 遞迴到最後面的 可能都會呼叫的upStairs(8) upStairs(7)等方法次數太多, // 既然計算多了那麼就快取嘛 修改 // 大概在40以後好像有點用,但是提升太不明顯 private static HashMap<String, Integer> numMap = new HashMap<String, Integer>(); public static int climbingStairsWithMap(int stairsNum) { if (stairsNum <= 0) { return 0; } else if (stairsNum == 1) { return 1; } else if (stairsNum == 2) { return 2; } if (numMap.containsKey(stairsNum)) { return numMap.get(stairsNum); } else { int val = climbingStairsWithMap(stairsNum - 1) + climbingStairsWithMap(stairsNum - 2); numMap.put(String.valueOf(stairsNum), val); return val; } } //mdzz才發現這不就是斐波那契數列麼,,,,直接算就完事了,寫得可能糙了一些。。 public static int climbingStairsFib(int stairsNum) { if (stairsNum <= 0) { return 0; } else if (stairsNum == 1) { return 1; } else if (stairsNum == 2) { return 2; } int first = 1; int second = 2; int targetNum = 0; for (int i = 3; i <= stairsNum; i++) { targetNum = first + second; first = second; second = targetNum; } return targetNum; } // 高階爬樓梯了。智商不夠看題目看了好久,一直不明白要表達什麼,就按照我理解的記錄一下解釋好了對不對另說,看一眼英文 // On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed). // // Once you pay the cost, you can either climb one or two steps. // You need to find minimum cost to reach the top of the floor, // and you can either start from the step with index 0, or the step with index 1. // // 中文坑了個大爹。第二句 Once you pay the cost, you can either climb one or two steps. // 我理解就是 一旦你花費所在這個臺階的體力,也就是cost[i] 你就可以選擇爬一個臺階還是兩個臺階 // 然後所謂的走到頂就是走出陣列長度,走完陣列。 // 按照這個思路來。強行解釋一波看看 // // 輸入: cost = [10, 15, 20] // 輸出: 15 // 自己的解釋: 我選擇少的開始那就是 10 然後發現 後面選擇一個或者兩個臺階都需要再花費, // 可以是 10 + 15 + 兩步 直接走出 走完 也可以是 10 + 20 然後走出 應該沒人會選擇 10 + 15 + 20 吧 // 明顯 10 + 15 舒服 // 再看假如 從 15 開始呢 好像走一個 15 再選擇走兩步,就走到頂了 只需要花費 15 // 跟給出的結果一樣。 // // 示例 2: // 輸入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] // 輸出: 6 // 自己的解釋:要求花費最少,這麼長那麼就都選擇少的好了, // 第一步 從 cost[0] 開始 // 第二步 花費 1 選擇走兩步 從 cost[0] 走到 cost[2] // 第三步 花費 1 從 cost[2] 走到 cost[4] 反正都兩步都是1 選擇繼續走兩步 // 第四步 花費 1 繼續走,這個時候 遇到 100 和 1 果斷選 1 到了 cost[6] // 第五步 花費 1 繼續走,1 和 100 選擇 1 到了 cost[7] // 第六步 花費 1 繼續走 ,然後100 和 1 選擇 1 到了 最後一個節點 cost[9] // 第七步 花費 1 走出去! // 共計花費 6 // 例子 強行解釋通了 // 在分析下來 其實 可以分解一下 陣列長度 1 那麼應該直接結束 長度 2 那麼選擇短的 // 超過2的 那麼就可以分析了: // 消耗是一個累計的過程 其實不應該僅僅考慮當前的,用之前一個爬樓梯的邏輯倒過來推, // 最後一步可能是 上一個臺階 也可能上兩個臺階 // 這樣的話 假設現在到了n級臺階最小耗費是 F(n) 那麼之前一步的花費應該是等於 F(n-1) 或者 F(n-1)F(n-1); // 則可以有 F(n) = min(F(n-1),F(n-1)) + cost[n] n表示當前所在的臺階 // 要走完整個樓梯應該走到 n+1 級 或者之後。 // // 寫程式碼吧 public static int minCostClimbingStairs(int[] cost) { int ret = 0; int lenth = cost.length; if (lenth <= 1) { //直接解決 return 0; } else if (lenth == 2) { //一步走完,選擇最少的就好 return Math.min(cost[0], cost[1]); } else { //先定義初始值,後續用於儲存中間產生的值, int first = cost[0]; // F(n-2) int second = cost[1]; // F(n-1) for (int i = 2; i <= lenth; i++) { int curr = 0; if (i != lenth) { curr = cost[i]; } ret = Math.min(first, second) + curr; first = second; second = ret; } return ret; } } }

假如有朋友跟我一樣,中文題目看不懂,看看英文也許能更明白。