关于矩阵前缀和【矩阵前缀和:常数时间求一子矩阵的加和】
给一个m*n的矩阵,找到一个子矩阵使其里面的数字加和最大,输出这个最大值
首先想到暴力算法:
枚举矩阵行列,复杂度O(n4),然后对每个枚举的子矩阵,计算矩阵内的加和O(n2),总复杂度O(n6)
通过前缀和,可以把计算加和的时间变为O(1),总复杂度O(n4),还是有点大
优化:引入动态规划中的最大子区间问题
【类似的题目】
已知一个一维数组,如何求一区间使得区间内加和最大?暴力枚举左右端点的复杂度O(n2),但是动态规划可以O(n)完成
定义状态
dp[i]表示以下标 i 的元素结尾的某个子区间的最大加和
状态转移
对于以下标 i 元素结尾的子区间,必须选取下标i的元素,但是可以主动选择是否丢弃前面的区间
- 下标i元素 a[i]加在【以 i-1 下标结尾的最大和子区间】后面
- 下标 i 元素单独形成一个子区间
比较两个结果,选取大的作为当前 i 下标结尾的区间的答案
最后遍历区间右端点,找最大即可
回到矩阵的问题:
现在我们已知用O(n)时间求一个一维数组最大子区间的问题,那么对于矩阵,我们可以花费O(n2)的时间,枚举起始行和结束行
在起始行和结束行已经确定的情况下,只要确定起始列,和结束列,就能够确定一个矩阵,那么我们套用上面的动态规划,对所有的列,确定一个区间使得在这个区间内,被起始行和结束行夹逼的列区间,加和值最大(加和值通过前缀和可以O(1)查询)
确定起始列,结束列,相当于一维数组的区间左端点,右端点
这里枚举起始行,结束行,起到降维的效果
O(n2)枚举起始行,结束行,再用O(n)时间找到最大的区间,总复杂度O(n3)
代码
#include <bits/stdc++.h>
using namespace std;
int a[1509][1509], dp[1509]