最大m子段和
- 问题描述
- 解题思路
- 伪代码
- 代码实现
- 复杂度分析
问题描述
给定一个有n(n>0)个整数的序列,要求其m个互不相交的子段,使得这m个子段和最大。
输入:整数序列{nums},m。
输出:最大m子段和。
对于m=1的情况,即求最大子数组和
解题思路
我们采用动态规划的思路解决这个问题:
根据常用的状态表示经验我们可以记dp[k][i]为前i个元素的最大k子段和,记last[k][i]为前i个元素中以nums[i-1]结尾的最大k子段和。
对于last[k][i]不难发现有两种情况:
- 若第k子段长度大于1,即last[k][i]=last[k][i-1]+nums[i-1];
- 若第k子段长度等于1,即last[k][i]=max{last[k-1]}+nums[i-1].则last[k][i]=max{last[k][i-1]+nums[i-1],max{last[k-1]}+nums[i-1]}.
容易得到dp[k][i]=max{dp[k],[i-1],last[k],[i]};
初始化last[1][1]=dp[1][1]=nums[0],last[1][i]=max{last[1][i-1]+nums[i-1],nums[i-1]},dp[1][i] = max(dp[1][i - 1], last[1][i]).
所求的最大m子段和即为dp[m][n].
伪代码
FUNCTION MaxSubArrayM(nums, m) :
Beginn ← LENGTH(nums)DECLARE last[1:n][1:m], dp[1:n][1:m]last[1][1] ← nums[0]dp[1][1] ← nums[0]FOR j FROM 2 TO n :last[1][j] ← MAX(nums[j - 1], last[1][j - 1] + nums[j - 1])dp[1][j] ← MAX(dp[1][j - 1], last[1][j])End For// 处理多子段FOR i FROM 2 TO m :dp[i][0] ← INT_MINlast[i][i - 1] ← INT_MINFOR j FROM i TO n :option1 ← last[i][j - 1] + nums[j - 1] option2 ← dp[i - 1][j - 1] + nums[j - 1] last[i][j] ← MAX(option1, option2)dp[i][j] ← MAX(dp[i][j - 1], last[i][j])End ForEnd ForRETURN dp[m][n]
End MaxSubArrayM
代码实现
int MaxSubArrayM(vector<int>& nums,int m)
{vector<vector<int>>last(nums.size() + 1, vector<int>(m + 1)), dp(nums.size() + 1, vector<int>(m + 1));last[1][1] = dp[1][1] = nums[0];for (int i = 2; i <= nums.size(); i++){last[1][i] = max(nums[i - 1], nums[i - 1] + last[1][i - 1]);dp[1][i] = max(dp[1][i - 1], last[1][i]);}for (int i = 2; i <= m ; i++){dp[i][0] = last[i][i-1] = INT_MIN;for (int j = i; j <= nums.size(); j++){last[i][j] = max(last[i][j - 1] + nums[j - 1], dp[i - 1][j - 1] + nums[j - 1]);dp[i][j] = max(dp[i][j - 1], last[i][j]);}}return dp[m][nums.size()];
}
复杂度分析
时间复杂度:主要运行时间在两层for循环,故时间复杂度为O(mn).
空间复杂度:不难发现我们申请了m*n的两个vector,故空间复杂度为O(mn).