LC410. Split Array Largest Sum

把一个数组分成m个连续子数组(不能有空数组),求所有分法中,子数组sum的最大值的最小值。

方法1:容易想到的是动态规划

dp[i][j] = min(max(dp[k-1][j-1], sum[k][i]) 1 <= k <= i, dp[i][j]表示用前i个数字,分成j组,最大和的最小值

time:$O(nnm)$ space:$O(nm)$

class Solution {
public:
    typedef long long ll;
    ll INF = 1e15;
    int splitArray(vector<int>& nums, int m) {
        int n = nums.size();
        vector<vector<ll>> dp(n + 1, vector<ll>(m + 1, INF));
        dp[0][0] = 0;
        vector<ll> sums(n + 1, 0);
        for (int i = 0; i < n; ++i) sums[i + 1] = sums[i] + nums[i];
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                for (int k = 1; k <= i; ++k) {
                    dp[i][j] = min(dp[i][j], max(dp[k - 1][j - 1], sums[i] - sums[k - 1]));
                }
            }
        }
        return dp[n][m];
    }
};

方法2:贪心+二分

 check(LL sum, vector<int>& nu) 函数判断数组能否被分成m段,最大的和不超过sum,用了贪心的思路,cnt表示最少能分多少段,使最大和不超过sum,因此,分当前段的时候,取尽可能多的数,如果最后cnt <= m,说明我肯定能分出m段,如果cnt < m,只要拆开一些段就行,最大和并不会变坏。假如某个数大于sum,则无论怎么分,都无法分出m(m > 1)段。
然后用二分去找最小的sum,能使check返回true,相当于找lower_bound

class Solution {
public:
    typedef long long LL;
    int m;
    bool check(LL sum, vector<int>& nu) {
        LL nsum = 0;
        int cnt = 1;
        for (int i : nu) {
            if (i > sum) return false;
            if (i + nsum > sum) {
                cnt++;
                nsum = i;
            }
            else 
                nsum += i;
        }
        return cnt <= m;
    }
    int splitArray(vector<int>& nums, int m) {
        int n = nums.size();
        this->m = m;
        LL l = 0, r = 0;
        for (int i : nums) r += i;
        LL mid, ans = r;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (check(mid, nums)) {
                ans = min(ans, mid);
                r = mid - 1;
            }
            else l = mid + 1;
        }
        return ans;
    }
};
原文地址:https://www.cnblogs.com/betaa/p/12529990.html