最大字段和问题:
问题描叙:
给定由N个整数组成的序列(a1,a2,...,an),求该序列字段和的最大和。
问题很简短,做起来也不是很难,这里我们主要为了了解这么一种算法思想,然后再尝试求解其他变种问题。
思路:
设最大字段和为X,
X=max(a[i]+···+a[j]),0<=i,j<=n;//为某一子段之和
我们该如何选出这段子段:
我们设m[j]为从0到j之间的最大子段和,0<=j<=n;
m[j]的含义就是:以a[j]为结束元素的连续数组的最大子段和。
由此,X=max(m[0]···m[n]);
下面求m[j]:
1.当m[j-1]>0时,无论a[j]为何值,
m[j]=m[j-1]+a[j];
2.当m[j-1]<=0时,无论a[j]为何值,
m[j]=a[j];
自己想的解释:如果当前和为负数,会影响后续最大和,就像你站在地上和桌子上最终高度不同一样,虽为相同高度,但产生的效果不同,也就是上一和为负数时,会影响当前正数发挥最大作用。
有点玄学了,**如果大佬有更好解释欢迎指点。**
举例:
k: 1 2 3 4
a: 3 -4 2 10
m: 3 -1 2 12
代码:
int m[n+1];
b[1]=a[1];
int maxSum=a[1];
for(int i=2;i<=n;i++){
if(m[i-1]<=0){
m[i]=a[i];
}
else{
m[i]=m[i-1]+a[i];
}
maxSum=max(m[i],maxSum);
}
最大子段和扩展:
给定一个正整数和负整数组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。
返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。
示例:
输入:
[
[-1,0],
[0,-1]
]
输出: [0,1,0,1]
解释: 输入中标粗的元素即为输出所表示的矩阵
说明:
1 <= matrix.length, matrix[0].length <= 200
这最大子段和的思路去思考:
与之前不同的是之前单单只需考虑一条直线的数,但矩阵是有长和宽的,不过可以先试着去想:
要求0~M之间的最大矩阵和,可以先求0~j之间的最大矩阵和,0<=j<=n,最后再取最大值。
当前范围
0 1 -1 0 <-0
1 2 -1 3
1 -1 0 4 <-2
2 1 3 -1
要求一个矩阵和,我们可以把竖直方向上的数放入一个数组中sumarr (范围:0~N),这样就转换为了一个**最大子段和**,如上图:
sumarr[0] sumarr[1] sumarr[2] sumarr[3]
0+1+1 1+2+(-1) -1+(-1)+0 0+3+4
然后就是套用最大子段和了,sumarr就相当于a[].
代码实现:
vector<int> getMaxMatrix(vector<vector<int>>& matrix) {
int tt = 0, tl = 0, bb = 0, br = 0, gmax = INT_MIN;
for (int i = 0; i < matrix.size(); ++i) {
vector<int> sumarr(matrix[0].size(), 0); // 对每个起始行i,创建一个对应的sumarr数组,初始置0
for (int j = i; j < matrix.size(); ++j) {
int m = 0, curmax = INT_MIN;
for (int k = 0; k < matrix[0].size(); ++k) {
sumarr[k] += matrix[j][k]; // 加和当前行当前位的值到sumarr数组中。
if (curmax <= 0) { // 对sumarr[0,k]数组求最大字段和
m = k; // curmax<=0,说明以sumarr[k]结尾的最大字段和起点为自己
curmax = sumarr[k];
} else {
curmax += sumarr[k];
}
if (curmax > gmax) { // 当curmax大于gmax时,更新tt,tl,bb,br
gmax = curmax;
tt = i;
tl = m;
bb = j;
br = k;
}
}
}
}
return {tt, tl, bb, br};
}
代码与解释有点出入,但思路是一样的,参考leetcode,不完善的地方还请大家提出,互相进步。