「RQNOJ106」最大加权矩形(最大子矩阵和问题)
给定一个长度为n的一维的数组matrix[n],让求其最大matrix[i] + matrix[i+1] + … + matrix[j] = sum问题?
简单算法:
穷举法
先预处理map[n]表示从matrix[0]->matrix[n]的和
for(int i = 0 to n)
for(int j = i+1 to n)
{
int tmp = map[j] – map[i-1];
}
算法时间复杂度为O(n^2).
空间额外占据O(n)。
DP算法:
设max[j]为matrix[0….j]中的最大子段之和,max[j]当前只有两种情况:
1)最大子段一直连续到matrix[j]; (2)以matrix[j]为起点的子段。
注意!
如果当前最大子段没有包含matrix[j],如果没有包含的话,在算max[j]之前我们就已经算出来了。
得到max[j]的状态转移方程为:max[j] = max { max[j-1] + matrix[j], matrix[j]}
所求的最大子段和为max{ max[j], 0<=j<n}
进一步,我们可以将max[]数组用一个变量代替。因为每次计算max[i]之需要用到上一个max[i-1],故一个变量可以替代。
空间优化至O(1)
max = – (1<<31);
tmp = 0;
for(int i=1 to n)
{
if(tmp>0)
tmp += matrix[i];
else
tmp = matrix[i];
if(tmp>max)
max = tmp;
}
————————————————————–
给定一个二维的数组matrix[n][n],让求其最大子矩阵和问题?
原始算法:
遍历每一个子矩阵,左上角为[x,y],右下角为[c, r],可知子矩阵共有O(n^4);
而每个子矩阵算总和的算法复杂度为O(n^2);
—->原始算法复杂度为o(n^6),时间复杂度太高,但是却无额外空间。
改进算法二:
遍历每一个子矩阵,左上角为[x, y],右下角为[c, r],子矩阵共有O(n^4);
但是每个子矩阵算和,时间复杂度可以优化为O(1)。
利用到额外空间map[n][n],其中map[x][y]表示左上角为[0, 0],右下角为[x, y]的子矩阵和。
左上角为[x, y],右下角为[c, r],子矩阵的和 = map[c][r] – map[x-1][r] – map[x][r-1] + 2*map[c-1][r-1]。
时间复杂度总共为O(n^4);
额外空间O(n^2)。
改进算法三:
利用额外空间O(n^3),记录子矩阵map[i][j][y],第y列的第i行到第j行的和。
1)以i行开始,j行结束的子矩阵,总共有n个。计算每个子矩阵的算法复杂度为O(1)。
2)i行开始,j行结束。2变量,两个变量,复杂度为O(n^2)。
可知该算法可以用O(n^3)的时间复杂度算出,但其代价是空间复杂度也是O(n^3)。
改进算法四:
相比算法三,仅是优化了空间复杂度,将其降低为O(n^2)。
子矩阵和map[x][y]表示第y列从第1行到第i行的和。
算法三的map[i][j][k] = map[j][k] – map[i-1][k]即可。不用都记录下来
参考代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include<iostream> using namespace std; int n,mp[101][101],t,ans=-0x7fffffff; int main() { cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { cin>>t;mp[i][j]=mp[i][j-1]+t; } for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) { int sum=0; for(int k=1;k<=n;k++) { if(sum>0)sum+=mp[k][j]-mp[k][i-1]; else sum=mp[k][j]-mp[k][i-1]; ans=max(ans,sum); } } cout<<ans; return 0; } |