最大加权矩形~前缀和与dp的综合应用

题目描述

在这里插入图片描述

输入样例

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行的和(前缀和思想的应用))。

猜你喜欢

转载自blog.csdn.net/fatfairyyy/article/details/114399624
今日推荐