P3625 [APIO2009]采油区域(前缀和)

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3625 [APIO2009]采油区域(前缀和)相关的知识,希望对你有一定的参考价值。

P3625 [APIO2009]采油区域(前缀和)

前缀和的好题。

考虑将矩形分成三部分有多少情况。

一共只有六种。

然后就是预处理4个角的最大值前缀和值,和从第i行到第j行的最大前缀和值,第i列到第j列的最大前缀和值。

一般矩形范围在 n , m ≤ 1 0 3 n,m\\le 10^3 n,m103的,显然就是 O ( n m ) O(nm) O(nm)的解法。

时间复杂度: O ( n m ) O(nm) O(nm)

// Problem: P3625 [APIO2009]采油区域
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3625
// Memory Limit: 125 MB
// Time Limit: 1500 ms
// Date: 2021-08-24 14:24:53
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;

int read() {
	char ch=getchar();
	int f=1,x=0;
	while(ch<'0'||ch>'9') {
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}

const int MAXN=15e2+10; 

int n,m,k,ans;
int sum[MAXN][MAXN],a[MAXN][MAXN];
int lu[MAXN][MAXN],ld[MAXN][MAXN],rd[MAXN][MAXN],ru[MAXN][MAXN],hor[MAXN][MAXN],ver[MAXN][MAXN];

int gets(int x,int y,int a,int b) {
	return sum[a][b]+sum[x-1][y-1]-sum[x-1][b]-sum[a][y-1];
    //用 sum 计算一个子矩阵中的数字和
}

signed main() {
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			a[i][j]=read();
			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
		}
	}
	
	for(int i=k;i<=n;i++) {
		for(int j=k;j<=m;j++) {
			lu[i][j]=max(max(lu[i][j-1],lu[i-1][j]),gets(i-k+1,j-k+1,i,j));
		}
	}
	for(int i=k;i<=n;i++) {
		for(int j=m-k+1;j;j--) {
			ru[i][j]=max(max(ru[i][j+1],ru[i-1][j]),gets(i-k+1,j,i,j+k-1));
		}
	}
	for(int i=n-k+1;i;i--) {
		for(int j=1;j<=m;j++) {
			ld[i][j]=max(max(ld[i][j-1],ld[i+1][j]),gets(i,j-k+1,i+k-1,j));
		}
	}
	for(int i=n-k+1;i;i--) {
		for(int j=m-k+1;j;j--) {
			rd[i][j]=max(max(rd[i][j+1],rd[i+1][j]),gets(i,j,i+k-1,j+k-1));
		}
	}
	for(int i=1;i<=n-k+1;i++) {
		for(int j=1;j<=m-k+1;j++) {
			hor[i][i+k-1]=max(hor[i][i+k-1],gets(i,j,i+k-1,j+k-1));
		}
	}
	for(int len=k+1;len<=n;len++) {
		for(int i=1,j=i+len-1;j<=n;j++,i++) {
			hor[i][j]=max(hor[i+1][j],hor[i][j-1]);
		}
	}
	for(int i=1;i<=m-k+1;i++) {
		for(int j=1;j<=n-k+1;j++) {
			ver[i][i+k-1]=max(ver[i][i+k-1],gets(j,i,j+k-1,i+k-1));
		}
	}
	for(int len=k+1;len<=m;len++) {
		for(int i=1,j=i+len-1;j<=m;j++,i++) {
			ver[i][j]=max(ver[i+1][j],ver[i][j-1]);
		}
	}
    //预处理
	
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			ans=max(ans,hor[i+1][n]+lu[i][j]+ru[i][j+1]);
			ans=max(ans,hor[1][i]+ld[i+1][j]+rd[i+1][j+1]);
			ans=max(ans,ver[j+1][m]+lu[i][j]+ld[i+1][j]);
			ans=max(ans,ver[1][j]+ru[i][j+1]+rd[i+1][j+1]);
		}
	}
	for(int i=1;i<=n;i++) {
		for(int j=i+1;j<=n;j++) {
			ans=max(ans,hor[1][i]+hor[i+1][j]+hor[j+1][n]);
		}
	}
	for(int i=1;i<=m;i++) {
		for(int j=1;j<=m;j++) {
			ans=max(ans,ver[1][i]+ver[i+1][j]+ver[j+1][m]);
		}
	}
    //计算答案
	
	cout<<ans<<endl;
	return 0;
}

来归纳下,如果分成 2 2 2份,就是切一刀,显然只需预处理两个数组即可。

分成4份,原做法复杂度会爆炸,因为枚举的东西不止二维。

以上是关于P3625 [APIO2009]采油区域(前缀和)的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P3625 [APIO2009]采油区域枚举

APIO2009 采油区域

[APIO2009]采油区域

(采油区域)二维前缀和+动态规划关系+分类讨论

BZOJ 1177 [Apio2009]Oil(递推)

bzoj1177 [Apio2009]Oil