题目描述
输入样例
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
输出样例
15
AC Code
#include <iostream>
using namespace std;
const int maxn = 155;
int a[maxn][maxn] = {
0};
int main()
{
int n,ans = -65535;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
a[i][j] += a[i-1][j];
}
}
for(int i=1;i<=n;i++){
for(int k=1;k<=i;k++){
int tmp,f[maxn] = {
0};
for(int j=1;j<=n;j++){
tmp = a[i][j] - a[i-k][j];
f[j] = max(f[j-1] + tmp,tmp);
ans = max(ans,f[j]);
}
}
}
cout<<ans;
return 0;
}
解释
①大体的思路来自于“最大子段和”,只不过“最大子段和”是在一维进行计算,而“最大加权矩形”是在二维进行计算,因此,想到了使用前缀和的方法。
②前缀和:
如输入样例所示,输入为:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
对它使用前缀和,即a[i][j] += a[i-1][j];
的方法进行压缩,得到的矩阵为:
0 -2 -7 0
9 0 -13 2
5 1 -17 3
4 9 -17 1
使用tmp = a[i][j] - a[i-k][j];
即可得到第i行第j列的前k列数据。
③dp :
for(int i=1;i<=n;i++){
for(int k=1;k<=i;k++){
int tmp,f[maxn] = {
0};
for(int j=1;j<=n;j++){
tmp = a[i][j] - a[i-k][j];
f[j] = max(f[j-1] + tmp,tmp);
ans = max(ans,f[j]);
}
}
}
1)将dp的结果存储在变量ans中。
2)第一层i循环,是对矩阵的行进行循环。第二层k循环,是对第i行的前k列中的k进行循环,万请注意k一定不大于i,否则得到的结果一定是错的。 第三层循环j,对矩阵的列数进行遍历。
3)关于变量tmp和数组f:tmp用于存储第i行的前k列的和,f用于使用dp。
4)状体转移方程:f[j] = max(f[j-1] + tmp,tmp);
即当前权值的最大和为f[j-1]+tmp(当f[j-1]和tmp皆正时)或tmp(当tmp和f[j-1]皆负且tmp>f[j-1]时或当f[j-1]为负,tmp为正时)。
5)这种方法非常的巧妙,与最大子段和不同,最大子段和的状态转移方程维护的是和与不变量(dp[i] = max(dp[i-1]+a[i],a[i]),其中a[i]为数组的值,是预先知道的,不需要经过二次计算的),而最大加权矩形维护的是变量(dp[i] = max(dp[i-1]+tmp,tmp),其中tmp在每一次循环中是会改变的,因为其表示的值是第i行第j列前k行的和(前缀和思想的应用))。