对于最大子段和,就是取一个数组中(包括负整数)a1,a2,a3,...,an中和最大的一段。想法很简单,两个变量M,N,每次加一个ai的时候先判断M是否大于0,大于的时候加上ai,否则等于ai,然后每次判断N是否小于M,是的话就保存M。代码实现如下。
int MaxSubSum(int n,int *a)
{
int b = 0;
int sum = 0;
for (int i = 0; i < n; ++i)
{
if (b > 0) b += a[i];
else { b = a[i]; }
if (sum < b) sum = b;
}
return sum;
}
然后问题延伸到最大m子段和。这个m是说有m段子段(子段不相交)之和为最大。条件和上面一样。其实上面就是这个m取特殊值m=1的时候。先想到用动态规划去解,所以假设一个二维数组b[i,j]用来保存最大子段和。分析之后发现:b[i,j]的最后一个子段只有两种情况。一种是只有a[j],一种是不只有a[j].所以b[i,j]的递归公式为:
b[i,j]=b[i,j-1]+a[j] //包含aj
b[i,j]=b[i-1,t]+a[j] //(1<=t<=m) //不包含aj。
最后发现如果用二维数组其实每次只有i-1和i行用的到。所以直接用两个一维数组就可以解决问题。代码实现如下。
//对于最大m子段和,假设用b[i,j]标识。但经分析分为第i子段包含/不包含b[j]情况。所以仅用2个数组保存即可。公式
//if(包含b[j]) =>b[i,j-1]+a[j]
//else =>b[i-1,t]+a[j] //i ≤ j ≤ n,i − 1 ≤ t < j
int Max_m_SubSum(int m,int n,int *a) // 最大m字段和。参数:m:子段个数、n:元素个数、a:数组。
{
if (n < m || m < 1) return 0;
int * b = new int[n+1]; //保存当前最大。
int * c = new int[n+1];//保存前一次最大。
b[0] = 0;
c[1] = 0;
//init
for (int i = 1; i <= m; i++)//由1到m,为一个子段范围。
{
b[i] = b[i - 1] + a[i];// b每次加上一个ai。
c[i - 1] = b[i];//c[i-1]保存b[i].
int max = b[i];//max保存bi
for (int j = i + 1; j <= i + n - m; j++) //设置为i+n-m。
{
b[j] = b[j - 1] > c[j - 1] ? b[j - 1] + a[j] : c[j - 1] + a[j];//判断取当前最大加上aj还是前一个加上aj。
c[j - 1] = max;//此时c[i-1]更新。
if (max < b[j]) max = b[j];//如果max小于bj,更新max。
}
c[i + n - m] = max;//最后一个c[i+n-m]保存max,在下一次循环中使用比较。
}
int sum = 0;
for (int j = m; j <= n; j++)
{
if (sum < b[i]) sum = b[j];
}
return sum;
}