Preface
??今天早上刷微博,看到LeetCode中国微博发了这样一条状态:
??已经好久没做题练练手了,于是想试试。LeetCode上,该题目的地址为:https://leetcode.com/problems/max-sum-of-sub-matrix-no-larger-than-k/
Analysis
??想了一上午,也没想出什么头绪。后来我看 LeetCode 上有不少人已经做出提交了。并且,在discuss页面里,有人公布了详细的解释与代码。
??我看了一下,他这个解法是基于Kadane Algorithm了。于是,先得学习一下什么是Kadane Algorithm。
Kadane Algorithm
??Kadane Algorithm 用于解决对一列数组中,求其中子序列的和最大的值。Kadane 的代码很多,各种语言的也都有,我下面摘取这个网站上的C++
代码,理解分析一下:
#include <iostream>
#include <climits>
using namespace std;
#define MAX(X, Y) (X > Y) ? X : Y
#define POS(X) (X > 0) ? X : 0
int kadane(int* row, int len)
{
int x;
//拿数组的第一个元素出来,若其大于0,则另sum = row[0]
//若其小于或等于0,则令sum = 0,
int sum = POS(row[0]);
int maxSum = INT_MIN; //INT_MIN是<climits>文件定义的,代表int类型最小值:-2147483648
for (x = 0; x < len; ++x)
{
//Kadane 算法的核心部分
//maxSum用于记录最大的子序列和,并每一次与sum进行比较,若当sum比之前的maxSum要大,则将现在的sum值赋予maxSum
//sum每加一个值,跟0进行一次比较,若加完row[x]都小于0了,那么就直接将sum置为0,接着开始一个新的子序列,并进行求和
maxSum = MAX(sum, maxSum);
sum = POS(sum + row[x]);
}
return maxSum;
}
int main()
{
int N;
cout << "Enter the array length: ";
cin >> N;
int arr[N];
cout << "Enter the array: ";
for (int i = 0; i < N; i++)
{
cin >> arr[i];
}
cout << "The Max Sum is: "<<kadane(arr, N) << endl;
return 0;
}
2D Kadane Algorithm
??由于我们这一题是二维矩阵,并不是一维数组。因此,要将 kadane 算法扩展到2维上。同样作者也推荐了一个视频,是位印度哥们,讲解的非常好。视频在 YouTube 上,地址:https://www.youtube.com/watch?v=yCQN096CwWM,保证听几遍就懂。
??下面我就他讲解的,用 Excel 表格展示这个二维 kadane 算法的过程。
??如图下面所示的矩阵,黄色黄色部分,4×5 的大小。先定义几个变量:
??1. 变量 L : 代表遍历时,当前子矩阵的左边位置;
??2. 变量 R : 代表遍历时,当前子矩阵的右边位置;
??3. 右边浅绿色,与矩阵的 row数 相同的临时存储区,是将当前的 L 列、L+1 列、……、R?1 列、R 列,进行列相加,然后再用 kadane 算法判断相加得到的列数组(此时即为一维数组了,可以用一般意义上的 kadane 算法),求此时元素连续和最大的子数组,并与之前的最大值进行比较(这一点会在下面的过程中体现出来);
??4. 变量 currentSum : 当前 L、R 组成的子矩阵(注意:这个子矩阵的“行数量“与原来大矩阵相同),其中这个矩阵的子矩阵,产生的最大的和;
??5. 变量 maxSum : 纪录目前遍历下来的最大的子矩阵和;
??6. 变量 maxLeft : 纪录目前遍历下来的最大子矩阵的左边位置;
??7. 变量 maxRight : 纪录目前遍历下来的最大子矩阵的右边位置;
??8. 变量 maxUp : 纪录目前遍历下来的最大子矩阵的上面位置;
??9. 变量 maxDown : 纪录目前遍历下来的最大子矩阵的下面位置;
??注意:如果 currentSum 不大于 maxSum,则保持 maxSum、maxLeft、maxRight、maxUp、maxDown 这几个变量值不变。
??第一次遍历,L、R 都在矩阵的开始 0 处:
??第二次遍历, 此时将 R 向右移动一个位置到 1 处,保持 L 位置不变。将 L、R 两行之间的矩阵进行列相加,得到 3 6 0 0,求这个 3 6 0 0 序列的和最大子序列。
??很容易看出,最大值为9,所以 currentSum 为9,那么发现9比之前的 maxSum=4 要大,所以,此时将 9 给 maxSum=9。maxLeft=0 纪录此时的 L=0,maxRight=1 纪录此时的 R=1,maxUp 纪录此时最大子序列的上面开始位置:maxUp=0,maxDown 纪录此时最大子序列的下面结束位置:maxDown=1:
??第三次遍历:
??第四次遍历:
??第五次遍历:
??第六次遍历:
??第七次遍历:
??第八次遍历:
??第九次遍历:
??第十次遍历:
??第十一次遍历:
??第十二次遍历:
??第十三次遍历:
??第十四次遍历:
??第十五次遍历:
??经过十五次的遍历后,我们终于找到了这个矩阵,就是上图中红色区域部分。这个大矩阵(4×5) 的最大元素和为18。
??这就是2D kadane算法的过程。这个算法的空间复杂度为: O(row),时间复杂度为:O(column×column×row)
Find the max sum no more than K
??解决了如何寻找子矩阵的最大和问题,现在题目中还有一个限制。就是这个和不能大于给定的 K,这个作者也推荐了Quora上的一个帖子:Given an array of integers A and an integer k, find a subarray that contains the largest sum, subject to a constraint that the sum is less than k?。回答这个问题的人也是一位大神。