第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

1. 前言

動態(tài)規(guī)劃(Dymamic Programming,簡稱 DP)應(yīng)該是面試中出現(xiàn)頻率最多并且公認(rèn)難度最大的一類題型,動態(tài)規(guī)劃問題非常靈活,沒有統(tǒng)一的模板。動態(tài)規(guī)劃中最簡單的問題,例如爬樓梯,狀態(tài)轉(zhuǎn)移方程非常簡單,但是大部分問題都沒有通用的狀態(tài)轉(zhuǎn)移方程,所以如果缺乏對DP問題的練習(xí),在筆試和面試中就有卡殼無從下手的風(fēng)險。候選人的目標(biāo)應(yīng)該是盡可能的找到不同題目之間的相似點并舉一反三。

2. 最大子數(shù)組和

面試官提問:給定一個數(shù)組,求解數(shù)組中連續(xù)子樹組的最大和。

題目解析

求解最大子數(shù)組求和問題是來源于算法網(wǎng)站LeetCode的經(jīng)典題目,題目鏈接:https://leetcode.com/problems/maximum-subarray/。

我們以小型數(shù)據(jù)的數(shù)組作為樣例分析,什么是連續(xù)子數(shù)組的最大和:

樣例輸入:[-2,1,-3,4,-1,2,1,-5,4]

樣例輸出:6

樣例解釋:連續(xù)子數(shù)組 [4,-1,2,1] 的和最大,求和結(jié)果為6。

這里需要特別明確子樹組和子序列的區(qū)別,

(1)子樹組定義:一個或者連續(xù)多個數(shù)組中元素組成一個子數(shù)組;
(2)子序列定義:在原來數(shù)組中找出一部分組成的序列。

結(jié)論:子樹組一定是連續(xù)的數(shù)字,但是子序列只需要保證前后順序上的有序,數(shù)字之間不一定連續(xù)。

在明確子數(shù)組定義之后,我們用一句話概括 DP 的核心解題思路:我們要找到某個子問題的最優(yōu)解,然后在它的幫助下,找到下一個子問題的最優(yōu)解。

例如對于爬樓梯問題,對于 n 個臺階,每次能夠跳一個臺階或者兩個臺階,我們知道對于第 k 個臺階,一定是從第 k-1 個臺階再跳一個臺階,或者是從第k-2個臺階再跳兩個臺階上來,所以只需要知道第 k-1 個臺階的路徑個數(shù)和第k-2個臺階的路徑個數(shù),求和就是最終的答案。其中第 k-1 和第 k-2 個臺階的路徑就是子問題。

將上述的語義切換到算法上來,就是需要找到狀態(tài)轉(zhuǎn)移方程以及狀態(tài)的初始值。

最大子樹組和問題中,狀態(tài)的初始含義非常明確:如果數(shù)組的長度為空,那么不存在子樹組,直接返回最大和為零。

初始狀態(tài)結(jié)果為零的含義是,最后返回的最大子樹組和一定不是負(fù)數(shù),因為和為負(fù)數(shù)還不如直接用空數(shù)組返回零。

對于一般子問題,我們假設(shè) dp[i] 表示以 nums[i] 作為子樹組末尾元素的前i個數(shù)字構(gòu)成的子樹組的最大和,nums[i]表示數(shù)組中的第i個元素。舉例來說,對于數(shù)組[a1,a2,a3],dp[2] 只有三種構(gòu)成可能:[a1,a2,a3][a2,a3],[a3],都是以 a3 作為子數(shù)組結(jié)尾元素。

那么假設(shè)已經(jīng)遍歷到了第i個數(shù)字,如果 dp[i-1] 小于零,說明數(shù)組的前 i-1 個子樹組的和最大也是負(fù)數(shù),前面說過,負(fù)數(shù)對于求解結(jié)果是沒有幫助的,所以如果 dp[i-1] 小于零,dp[i] 直接選擇 nums[i] 作為只有一個元素的子數(shù)組,和最大值就是 nums[i]。如果 dp[i-1] 大于零,說明加上前面的子樹組會讓結(jié)果更大,那么需要加上。

上面的語義用dp方程表示如下:

dp[i] = 0 如果nums.length=0;

dp[i] = nums[i] 如果dp[i-1] < 0;

dp[i] = nums[i] + dp[i-1],如果dp[i-1] > 0 。

最后,我們需要返回的是 dp 數(shù)組中的最大值。

從狀態(tài)轉(zhuǎn)移方程可以看出,我們其實只需要 dp[i-1],所以使用一個臨時變量去保存它,能將空間復(fù)雜度降低到O(1)。示例:

public int maxSubArray(int[] nums) {
    //邊界條件判斷
    if (nums.length == 0){
        return 0;
    }
    //表示dp[i-1]
    int prev = nums[0];
    //表示dp[i]
    int cur = nums[0];
    //結(jié)果
    int max = nums[0];
    
    for (int i = 1; i < nums.length; i++){
        if (prev > 0){
            //如果dp[i-1] > 0
            cur = prev + nums[i];
        } else {
            //如果dp[i-1] <= 0
            cur = nums[i];
        }
        max = Math.max(max, cur);
        prev = cur; 
    }
    
    return max;
}

3. 小結(jié)

本章節(jié)介紹了動態(tài)規(guī)劃中最簡單的一種問題,就是在數(shù)組中找到滿足要求的極大值或者極小值,通常只需要構(gòu)建一個簡潔的狀態(tài)轉(zhuǎn)移方程就可以解決問題。如果狀態(tài)轉(zhuǎn)移方程中只涉及到連續(xù)的兩個或者三個位置的值變化,甚至可以將狀態(tài)數(shù)組壓縮到單個變量,將空間復(fù)雜度從O(n)優(yōu)化到O(1)。