[APIO2009]采油区域

题目描述

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

说明

数据保证 K≤M 且 K≤N 并且至少有三个 K×K 的互不相交的正方形区域。

其 中 30%的输入数据,M, N≤ 12。所有的输入数据, M, N≤ 1500。每一小块土地的 石油储量的估计值是非负整数且≤ 500。

题解

弱化版的,可以爆搜即可。

这个是增强版,要dp

可以发现(很难想到),把这个原来的矩形选择3个k*k的正方形区域,

如果我们把大矩形分成3块,总有一种切的方法,可以使得这3个选择的k*k的正方形区域,在每个小的块内都有一块。

一共有6种方法:图片来源

其中,每个正方形在一个小块内随便动。

对于每一个1~6的情况,我们要枚举所有这种形态下的所有情况,计算出最大值,再取max

直接暴力显然不可取。

显然(难以)想到,每个块(除了5,6)都是和边界相交的。

以下所有的i,j表示k*k矩形的右下角,姑且叫代表点

所以,我们设a[i][j],b[i][j],c[i][j],d[i][j],表示,这个代表点在(i,j)左上、右上,左下,右下的所有情况中,k*k正方形最大的总和。

对于a,b,c,d我们都可以以合理的方式递推得到。

然后,再6次nm枚举6种形态的所有情况,取一个mx

 

注意,(i,j)是代表点的坐标,所以我们循环的边界要注意。必须留出3个正方形的空间。

画图举例想一想就很容易了。

 

代码:(之后统计的编号对应在图中,都加了注释)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1505;
int s[N][N],a[N][N],b[N][N],c[N][N],d[N][N];
int ans,n,m,k;
int main(){
    scanf("%d%d%d",&n,&m,&k);int t;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&t),s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+t;
    for(int i=n;i>=k;i--)
        for(int j=m;j>=k;j--)
            s[i][j]-=s[i-k][j]+s[i][j-k]-s[i-k][j-k];
    for(int i=k;i<=n;i++)
        for(int j=k;j<=m;j++)
            a[i][j]=max(s[i][j],max(a[i-1][j],a[i][j-1]));
    for(int i=k;i<=n;i++)
        for(int j=m;j>=k;j--)
            b[i][j]=max(s[i][j],max(b[i][j+1],b[i-1][j]));
    for(int i=n;i>=k;i--)
        for(int j=k;j<=m;j++)
            c[i][j]=max(s[i][j],max(c[i][j-1],c[i+1][j]));
    for(int i=n;i>=k;i--)
        for(int j=m;j>=k;j--)
            d[i][j]=max(s[i][j],max(d[i][j+1],d[i+1][j]));
    
    

  for(int i=k;i<=n-k;i++)//1
        for(int j=k;j<=m-k;j++)
            ans=max(ans,a[i][j]+b[i][j+k]+c[i+k][m]);
    for(int i=k+k;i<=n;i++)//2
        for(int j=k;j<=m-k;j++)
             ans=max(ans,c[i][j]+d[i][j+k]+a[i-k][m]);
    for(int i=k+k;i<=n-k;i++)//6
        for(int j=k;j<=m;j++)
            ans=max(ans,s[i][j]+a[i-k][m]+c[i+k][m]);
    for(int i=k;i<=n-k;i++)//3
        for(int j=k;j<=m-k;j++)
            ans=max(ans,a[i][j]+c[i+k][j]+b[n][j+k]);
    for(int i=k;i<=n-k;i++)//4
        for(int j=k+k;j<=m;j++)
            ans=max(ans,a[n][j-k]+b[i][j]+d[i+k][j]);
    for(int i=k;i<=n-k;i++)//5
        for(int j=k+k;j<=m-k;j++)
            ans=max(ans,s[i][j]+a[n][j-k]+b[n][j+k]);
    printf("%d",ans);
    return 0;
}    

 

转载于:https://www.cnblogs.com/Miracevin/p/9657177.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值