P3625 [APIO2009] 采油区域题解

原题传送门

分析

这道题是典型的二维前缀和例题。对于这个问题,题目要求我们求出三个 k × k k \times k k×k 个范围区域内总和的最大值

对此,我们有上面的六种取法,在不同的情况下取最大值。以左上、右上、左下、右下四个方向为起点,并求出分别起点到 ( i , j ) (i,j) (i,j) 的二维前缀和,在到达 ( i , j ) (i,j) (i,j) 的区域后,计算 k × k k \times k k×k 的正方形最大值即可。

AC code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;//数据范围 
int n,m,k,ans;//n,m,k看题,ans为最大值
int mp[maxn][maxn],sum[maxn][maxn];//mp为矩形,sum为二维前缀和 
int lu[maxn][maxn],ld[maxn][maxn],ru[maxn][maxn],rd[maxn][maxn];//lu,ld,ru,rd表示四个方向 
int l[maxn];
inline int alc(int x,int y)
{
	return sum[x][y] - sum[x - k][y] - sum[x][y - k] + sum[x - k][y - k];//求k*k范围面积值 
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	cin>>n>>m>>k;//输入 
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= m;j++)
		{
			cin>>mp[i][j];//输入 
		}
	}
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= m;j++)
		{
			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + mp[i][j];//计算前缀和 
		}
	}
	//四个方向预处理计算最大值再汇总 
	for(int i = k;i <= n;i++)
	{
		for(int j = k;j <= m;j++)
		{
			lu[i][j] = max(alc(i,j),max(lu[i][j - 1],lu[i - 1][j]));//计算左上最大值 
		}
		for(int j = m;j >= k;j--)
		{
			ru[i][j - k] = max(alc(i,j),max(ru[i - 1][j - k],ru[i][j - k + 1]));//计算右上最大值
		}
	}
	for(int i = n;i >= k;i--)
	{
		for(int j = k;j <= m;j++)
		{
			ld[i - k][j] = max(alc(i,j),max(ld[i - k][j - 1],ld[i - k + 1][j]));//计算左下最大值
		}
		for(int j = m;j >= k;j--)
		{
			rd[i - k][j - k] = max(alc(i,j),max(rd[i - k][j - k + 1],rd[i - k + 1][j - k]));//计算右下最大值
		}
	}
	//然后六种情况暴力取最大值 
	for(int i = k;i <= n - k + 1;i++)
	{
		for(int j = k;j <= m - k + 1;j++)
		{
			ans = max(ans,lu[i][m] + ld[i][j] + rd[i][j]);//第一种情况 
			ans = max(ans,lu[i][j] + ru[i][j] + ld[i][m]);//第二种情况
			ans = max(ans,lu[i][j] + ld[i][j] + ru[n][j]);//第三种情况 
			ans = max(ans,lu[n][j] + ru[i][j] + rd[i][j]);//第四种情况
		}
	}
	for(int i = k;i <= n - k;i++)
	{
		for(int j = 1;j <= m - k;j++)
		{
			ans = max(ans,lu[i][m] + ld[i + k][m] + alc(i + k,j + k));//第五种情况
		}
	}
	for(int i = 1;i <= n - k;i++)
	{
		for(int j = k;j <= m - k;j++)
		{
			ans = max(ans,lu[n][j] + ru[n][j + k] + alc(i + k,j + k));//第六种情况
		}
	}
	cout<<ans<<endl;//输出 
	return 0;//完结,撒花 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值