1. 程式人生 > 實用技巧 >53. 最大子序和(C++)

53. 最大子序和(C++)

目錄

題目

給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4]
輸出: 6
解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。

分析與題解

暴力迴圈

比較容易想到的是暴力解題,即窮舉所有的子區間:

  • 使用雙層迴圈,窮舉所有的子區間;
  • 然後再對子區間內的所有元素求和;
  • 時間複雜度是立方級別

程式碼如下:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int max = INT_MIN;
        for(int i=0;i<n;i++){
            int sum=0;
            for(int j=i;j<n;j++){
                sum += nums[j];
                if(sum > max)
                    max = sum;
            }
        }
        return max;
    }
};

需要注意邊界的問題:首先在外層迴圈遍歷陣列,作為求非連續數列的起點,然後從此出發逐個元素相加,每新增一個新的元素,sum值進行變化都需要與最終的結果進行比較,取較大值,最終一直新增到元素末位。

動態規劃(狀態轉移方程1)

將問題切分為最優子問題進行求解。

①定義狀態

我們使用一個數組或者雜湊表dp儲存子問題的答案。例如dp[i]代表以nums[i]結尾的連續子陣列的最大和。

②思考轉移狀態方程

根據狀態的定義,由於 nums[i] 一定會被選取。並且 dp[i]所表示的連續子序列與 dp[i - 1] 所表示的連續子序列(有可能)就差一個 nums[i]

先考慮最簡單的情況,假設陣列 nums 全是正數,那麼一定有 dp[i] = dp[i - 1] + nums[i]

但在一般情況下 dp[i - 1] 有可能是負數,例如前幾個數都是負數,突然來了一個正數。於是分類討論:

  • 如果 dp[i - 1] >= 0,那麼可以把 nums[i] 直接接在 dp[i - 1] 表示的那個陣列的後面。
  • 如果 dp[i - 1] < 0,那麼加上前面的數反而越來越小了,不如以nums[i]為起點作為dp[i]數值。

狀態轉移方程即為:

\[d p[i]=\left\{\begin{array}{ll} d p[i-1]+n u m s[i], & \text { if } \quad d p[i-1] \geq 0 \\ \text { nums }[i], & \text { if } \quad d p[i-1]<0 \end{array}\right.\]

程式碼如下:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int sum = 0;
        int result = INT_MIN;
        for(int i=0;i<n;i++){
            sum+=nums[i];
            result = max(result, sum);
            if(sum < 0)
                sum = 0;
        }
        return result;
    }
};

自底向上(狀態轉移方程2)

對於dp[i-1]的討論,最終無非是求子序列和的最大值,因此狀態轉移方程可以簡寫為:

\[d p[i]=\max \{n u m s[i], d p[i-1]+n u m s[i]\} \]

因此我們不再討論dp[i-1]的正負,直接將是/否新增dp[i-1]的值比較並取較大值。程式碼如下:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        if(n==0) return 0;
        vector<int> dp(n);
        dp[0] = nums[0];
        int result = dp[0];
        for(int i=1;i<n;i++){
            dp[i] = max(dp[i-1]+nums[i], nums[i]);
            result = max(result, dp[i]);
        }
        return result;
    }
};

需要注意的是狀態dp代表的是以nums[i]結尾的子序列和的最大值,但是對於所給序列的最大值並不一定以包含末尾元素,所以需要設定額外的變數來進行記錄和比較。