Skip to content

【动规】最大子数组和、积

最大子数组和

经典1D动态规划。

Kadane algorithm, time = \(O(n)\):

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int ans = nums[0];
        int dp = nums[0]; // space optimization
        for (int i = 1; i < nums.size(); i++) {
            dp = max(dp + nums[i], nums[i]);
            ans = max(ans, dp);
        }
        return ans;
    }
};

最大子矩阵

给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。

返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。

2D拓展。

但并不是用2D前缀和,反而是1D列前缀和,再枚举列起点和终点,再对行做1D Kadane。这样能够时间复杂度\(O(n^2m)\)

另外不只要求返回和,还要返回坐标,需要额外的记录和判断。

class Solution {
public:
    vector<int> getMaxMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<vector<int>> s(m + 1, vector<int>(n, 0)); // column-wise prefix sum
        for (int i = 1; i <= m; i++) {
            for (int j = 0; j < n; j++) {
                s[i][j] = s[i-1][j] + matrix[i - 1][j];
            }
        }
        int mx = matrix[0][0];
        vector<int> ans(4, 0);
        // row start/end, O(n^2)
        for (int r1 = 0; r1 < m; r1++) {
            for (int r2 = r1 + 1; r2 <= m; r2++) {
                // kadane on each row, O(m)
                int dp = s[r2][0] - s[r1][0];
                int start = 0; // start index of max-sum-subarray
                if (dp > mx) mx = dp, ans = {r1, 0, r2 - 1, 0};
                for (int c = 1; c < n; c++) {
                    int v = s[r2][c] - s[r1][c];
                    if (dp + v > v) {
                        dp = dp + v;
                    } else {
                        dp = v;
                        start = c;
                    }
                    if (dp > mx) mx = dp, ans = {r1, start, r2 - 1, c};
                }
            }
        }
        return ans;
    }
};

乘积最大子数组

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

测试用例的答案是一个 32-位 整数。

子数组 是数组的连续子序列。

乘法拓展,如果所有数都是正数,乘法仍然保持Kadane的动态规划条件,可以直接用。

然而如果有负数,就得再记录一个最小值,因为有可能子数组包含偶数个负数,积仍为正数。

Variant of Kadane, time = \(O(n)\):

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int ans = nums[0];
        int mx = nums[0], mn = nums[0];
        for (int i = 1; i < nums.size(); i++) {
            int old_mx = mx;
            mx = max(mx * nums[i], max(mn * nums[i], nums[i]));
            mn = min(mn * nums[i], min(old_mx * nums[i], nums[i]));
            ans = max(ans, mx);
        }
        return ans;
    }
};