问题描述
给定由n个整数(可能为负数)组成的序列a1,a2,a3……an,以及一个正整数m,要求确定此序列的m个不相交子段的总和达到最大。最大子段和问题是最大m字段和问题当m=1时的特殊情形。
思考过程
参考文章:http://www.cnblogs.com/chuckcpc/p/dp_Max_M_Sum.html
参考文献:http://blog.163.com/sentimental_man/blog/static/7300161820119109533172/
接触到的这类题大概分为两类:
子段、子串DP:要求连续
子序列DP:序列——不连续
这两类问题区别不大:
需要考虑当前位置a[i]是否需要加入段,子问题是a[i-1]是否需要加入段
回到这道题,这道题目的状态定义比较复杂,而且不易想到
令dp[i][j]表示前j个元素,分成i段所能构成的最大值,同时这个最大值是由第j个元素a[j]结束的。
状态转移方程是: dp[i][j]=max{f[i][j-1]+a[j],f[i-1][k]+a[j],(i-1<=k< j)} (i<=j<=n-m+i)
重点在与这个状态的定义使得遍历的范围、以及产生解的过程比较玄乎。
由第a[j]个元素结束的意思是:
a[j]个元素要么是属于第i个子段,要么是属于第i-1个子段。
下界:如果m=n=4,这时dp[2][4]求出来了,意思是前面的四个元素分成了两段,当是还有两段要分,所以求出这个是没有意义的。当然求出来也不会影响结果,只是这样时间复杂度就提高了。
加粗的话看了好久….
终于差不多看懂了…
class Solution{
public:
void get_M_subarr(vector<int> &nums,int m){
int n=nums.size();
int dp[m+1][n];
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++){
dp[i][i-1]=nums[i-1];
for(int j=i;j<n-m+i;j++){
//求出i~j-1的最大值
int maxV=dp[i-1][i+1];
for(int k=i+1;k<j;k++){
if(dp[i-1][k] > maxV)
maxV=dp[i-1][k];
}
dp[i][j]=max(dp[i][j-1]+nums[j],maxV+nums[j]);
}
}
// for(int i=1;i<=m;i++){
// for(int j=0;j<n;j++)
// printf("%d ",dp[i][j]);
// printf("\n");
// }
// printf("\n");
printf("%d\n",dp[m][n-1]);
}
};