程序员面试金典——18.12最大和子矩阵
Solution1:
参考网址:
[1]https://www.cnblogs.com/GodA/p/5237061.html 思想讲的很清楚~
[2]https://www.nowcoder.com/profile/963612/codeBookDetail?submissionId=15224519
思路:
动态规划
我们来分析一下最优子结构,若想找到n个数的最大子段和,那么要找到n-1个数的最大子段和,这就出来了。我们用b[i]来表示a[0]…a[i]的最大子段和,b[i]无非有两种情况:
(1)最大子段一直连续到a[i]
(2)以a[i]为首的新的子段 。
由此我们可以得到b[i]的状态转移方程:
b[i]=max{b[i−1]+a[i],a[i]}
b
[
i
]
=
m
a
x
{
b
[
i
−
1
]
+
a
[
i
]
,
a
[
i
]
}
。最终我们得到的最大子段和为
max{b[i],0<=i<n}
m
a
x
{
b
[
i
]
,
0
<=
i
<
n
}
,
这跟最大子矩阵有什么关系呢?当然有关系学啦!二维就是一维的扩展,把二维压扁不就变成一维了吗?
我们假设所求N*N的矩阵的最大子矩阵是从i列到j列,q行到p行,如下图所示(假设下标从1开始)
a[1][1] a[1][2] ······ a[1][i] ······ a[1][j] ······ a[1][n]
a[2][1] a[2][2] ······ a[2][i] ······ a[2][j] ······ a[2][n]
······
a[q][1] a[q][2] ······ a[q][i] ······ a[q][j] ······ a[q][n]
······
a[p][1] a[p][2] ······ a[p][i] ······ a[p][j] ······ a[p][n]
······
a[n][1] a[n][2] ······ a[n][i] ······ a[n][j] ······ a[n][n]
最大子矩阵就是对角线为a[q][i]-a[p][j]的子矩阵,如果把最大子矩阵同列的加起来,我们可以得到一个一维数组
{a[q][i]+⋅⋅⋅⋅⋅⋅+a[p][i],⋅⋅⋅⋅⋅⋅,a[q][j]+⋅⋅⋅⋅⋅⋅+a[p][j]}
{
a
[
q
]
[
i
]
+
·
·
·
·
·
·
+
a
[
p
]
[
i
]
,
·
·
·
·
·
·
,
a
[
q
]
[
j
]
+
·
·
·
·
·
·
+
a
[
p
]
[
j
]
}
,现在我们可以看出,这其实就是一个一维数组的最大子段问题。如果把二维数组看成是纵向的一维数组和横向的一维数组,那问题不就迎刃而解了吗?把二维转换成了我们刚刚解决了的问题。
class SubMatrix {
public:
int sumOfSubMatrix(vector<vector<int> > mat, int n) {
// write code here
if(n <= 0)
return 0;
int maxVal = INT_MIN;
for(int i = 0; i < n; i++) {
vector<int> temp(mat[i]); //temp是一维行向量
maxVal = max(maxVal, helper(temp));//先得到第i行的最大和
for(int j = i + 1; j < n; j++) { //依次循环下面的n-i行,把每行里的n个数加到第i行中
for(int k = 0; k < n; k++) { //i+1 =< j <= n-1,把第j行中n个数加到第i行中
temp[k] += mat[j][k];
}
maxVal = max(maxVal, helper(temp));//每当加完1行后就判断1次最大和
}
}
return maxVal;
}
//求连续数组最大和,动态规划思想
int helper(vector<int>& vec) { //求一维数组最大和
int f = vec[0];
int result = f;
for(int i = 1; i < vec.size(); i++) {
f = max(f+vec[i], vec[i]);
result = max(result, f);
}
return result;
}
};
PS:一道很不错的动态规划题,理解他着实费了点功夫