动态规划套路:最大子数组和
生活随笔
收集整理的這篇文章主要介紹了
动态规划套路:最大子数组和
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
動態規劃套路:最大子數組和
文章目錄
- 動態規劃套路:最大子數組和
- 一、題目描述
- 二、分析
一、題目描述
這次看一個簡答的題:
二、分析
這道題比較簡單,主要是回顧動態 規劃的解法!
- 其實第一次看到這道題,我首先想到的是滑動窗口算法,因為我們說過,滑動窗口算法就是專門處理子串/子數組問題的,這里不就是子數組問題么?
- 但是,稍加分析就發現,這道題還不能用滑動窗口算法,因為數組中的數字可以是負數。
- 滑動窗口算法無非就是雙指針形成的窗口掃描整個數組/子串,但關鍵是,你得清楚地知道什么時候應該移動右側指針來擴大窗口,什么時候移動左側指針來減小窗口。
- 而對于這道題目,你想想,當窗口擴大的時候可能遇到負數,窗口中的值也就可能增加也可能減少,這種情況下不知道什么時機去收縮左側窗口,也就無法求出「最大子數組和」。
- 解決這個問題需要動態規劃技巧,但是dp數組的定義比較特殊。按照我們常規的動態規劃思路,一般是這樣定義dp數組:
nums[0..i]中的「最大的子數組和」為dp[i]。
- 如果這樣定義的話,整個nums數組的「最大子數組和」就是dp[n-1]。如何找狀態轉移方程呢?按照數學歸納法,假設我們知道了dp[i-1],如何推導出dp[i]呢?
- 如下圖,按照我們剛才對dp數組的定義,dp[i] = 5,也就是等于nums[0…i]中的最大子數組和:
- 那么在上圖這種情況中,利用數學歸納法,你能用dp[i]推出dp[i+1]嗎?
- 實際上是不行的,因為子數組一定是連續的,按照我們當前dp數組定義,并不能保證nums[0..i]中的最大子數組與nums[i+1]是相鄰的,也就沒辦法從dp[i]推導出dp[i+1]。
- 所以說我們這樣定義dp數組是不正確的,無法得到合適的狀態轉移方程。對于這類子數組問題,我們就要重新定義dp數組的含義:
以nums[i]為結尾的「最大子數組和」為dp[i]。
- 這種定義之下,想得到整個nums數組的「最大子數組和」,不能直接返回dp[n-1],而需要遍歷整個dp數組:
- 依然使用數學歸納法來找狀態轉移關系:假設我們已經算出了dp[i-1],如何推導出dp[i]呢?
- 可以做到,dp[i]有兩種「選擇」,要么與前面的相鄰子數組連接,形成一個和更大的子數組;要么不與前面的子數組連接,自成一派,自己作為一個子數組。
- 如何選擇?既然要求「最大子數組和」,當然選擇結果更大的那個啦:
綜上,我們已經寫出了狀態轉移方程,就可以直接寫出解法了:
int maxSubArray(int[] nums) {int n = nums.length;if (n == 0) return 0;int[] dp = new int[n];// base case// 第一個元素前面沒有子數組dp[0] = nums[0];// 狀態轉移方程for (int i = 1; i < n; i++) {dp[i] = Math.max(nums[i], nums[i] + dp[i - 1]);}// 得到 nums 的最大子數組int res = Integer.MIN_VALUE;for (int i = 0; i < n; i++) {res = Math.max(res, dp[i]);}return res; }以上解法時間復雜度是 O(N),空間復雜度也是 O(N),較暴力解法已經很優秀了,不過注意到dp[i]僅僅和dp[i-1]的狀態有關,那么我們可以進行「狀態壓縮」,將空間復雜度降低:
int maxSubArray(int[] nums) {int n = nums.length;if (n == 0) return 0;// base caseint dp_0 = nums[0];int dp_1 = 0, res = dp_0;for (int i = 1; i < n; i++) {// dp[i] = max(nums[i], nums[i] + dp[i-1])dp_1 = Math.max(nums[i], nums[i] + dp_0);dp_0 = dp_1;// 順便計算最大的結果res = Math.max(res, dp_1);}return res;總結
以上是生活随笔為你收集整理的动态规划套路:最大子数组和的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Django环境搭建
- 下一篇: 力扣--- 滑动谜题