采油区域 前缀和+DP

11 篇文章 1 订阅
4 篇文章 0 订阅


算法训练  采油区域    
时间限制:2.0s     内存限制:512.0MB 
      
  采油区域  Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井。被拍卖的整块土地为一个矩形区域,被划分为M×N个小块。 
  Siruseri地质调查局有关于Navalur土地石油储量的估测数据。这些数据表示为M×N个非负整数,即对每一小块土地石油储量的估计值。 
  为了避免出现垄断,政府规定每一个承包商只能承包一个由K×K块相连的土地构成的正方形区域。 
  AoE石油联合公司由三个承包商组成,他们想选择三块互不相交的K×K的区域使得总的收益最大。 
  例如,假设石油储量的估计值如下: 
1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 
1 8 8 8 8 8 1 1 1 
1 8 8 8 8 8 1 1 1 
1 8 8 8 8 8 1 1 1 
1 1 1 1 8 8 8 1 1 
1 1 1 1 1 1 8 8 8 
1 1 1 1 1 1 9 9 9 
1 1 1 1 1 1 9 9 9 



  如果K  =  2,  AoE公司可以承包的区域的石油储量总和为100,  如果K  =  3,  AoE公司可以承包的区域的石油储量总和为208。 
  AoE公司雇佣你来写一个程序,帮助计算出他们可以承包的区域的石油储量之和的最大值。 
输入格式 
  输入第一行包含三个整数M,  N,  K,其中M和N是矩形区域的行数和列数,K是每一个承包商承包的正方形的大小(边长的块数)。接下来M行,每行有N个非负整数表示这一行每一小块土地的石油储量的估计值。 
输出格式 
  输出只包含一个整数,表示AoE公司可以承包的区域的石油储量之和的最大值。 
数据规模和约定 
  数据保证K≤M且K≤N并且至少有三个K×K的互不相交的正方形区域。其中30%的输入数据,M,  N≤  12。所有的输入数据,  M,  N≤  1500。每一小块土地的石油储量的估计值是非负整数且≤  500。 
样例输入 
9  9  3 
1  1  1  1  1  1  1  1  1 
1  1  1  1  1  1  1  1  1 
1  8  8  8  8  8  1  1  1 
1  8  8  8  8  8  1  1  1 
1  8  8  8  8  8  1  1  1 
1  1  1  1  8  8  8  1  1 
1  1  1  1  1  1  8  8  8 
1  1  1  1  1  1  9  9  9 
1  1  1  1  1  1  9  9  9 
样例输出 
208 

思路:对于三个区域的分布进行考虑,只存在6种情况:

 


      

  

只要找到6种情况中的三个区域中最大的值即可,然而对于case1的所有情况有 1500*1500,因此要找到三个区域中的最大值的时间维持在常数内。此时可预处理,用前缀和将以左上角和(i,j)为对角的矩形内的值的和保存在(i,j),随后将k*k的区域值的最大值保存在(i,j),再分别求出以右下,右上,左上顶点为最大值即可。而对于case1和case2,可以规定中间的区域的宽度就是k,此时最优的情况依然包含在内。

code:

#include<iostream>
using namespace std;

const int MAX_N=1505;
const int MAX_M=1505;
int n,m,k,ans;
int d[MAX_N][MAX_M];
int a[MAX_N][MAX_M],aa[MAX_N][MAX_M];
int b1[MAX_N][MAX_M],b2[MAX_N][MAX_M],b3[MAX_N][MAX_M],b4[MAX_N][MAX_M];

int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m>>k;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
		{
			cin>>d[i][j];
			a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+d[i][j];
			aa[i][j]=a[i][j]-a[i][j-k]-a[i-k][j]+a[i-k][j-k];
			if(i>=k&&j>=k)	b1[i][j]=max(aa[i][j],max(b1[i][j-1],b1[i-1][j]));
		}
	for(int i=k;i<=n;++i)
		for(int j=m-k+1;j>=1;--j)
			b2[i][j]=max(aa[i][j+k-1],max(b2[i][j+1],b2[i-1][j]));	
	for(int i=n-k+1;i>=1;--i)
		for(int j=k;j<=m;++j)
			b3[i][j]=max(aa[i+k-1][j],max(b3[i][j-1],b3[i+1][j]));
	for(int i=n-k+1;i>=1;--i)
		for(int j=m-k+1;j>=1;--j)
			b4[i][j]=max(aa[i+k-1][j+k-1],max(b4[i][j+1],b4[i+1][j]));	
	for(int i=k;i+k<=n;++i)
		for(int j=k;j+k<=m;++j)
		{
			int aa=max(b1[n][j]+b2[i][j+1]+b4[i+1][j+1],b1[i][j]+b2[n][j+1]+b3[i+1][j]);
			int bb=max(b1[i][j]+b2[i][j+1]+b3[i+1][m],b1[i][m]+b3[i+1][j]+b4[i+1][j+1]);
			ans=max(ans,max(aa,bb));
		}
	for(int i=2*k;i+k<=n;++i)
		for(int j=k;j<=m;++j)
			ans=max(ans,b1[i-k][m]+b3[i+1][m]+aa[i][j]);
	for(int j=2*k;j+k<=m;++j)
		for(int i=k;i<=n;++i)
			ans=max(ans,b1[n][j-k]+b2[n][j+1]+aa[i][j]);
	cout<<ans<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值