zoukankan      html  css  js  c++  java
  • [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;
    }    
  • 相关阅读:
    简单理解ThreadLocal原理和适用场景
    Portal实现原理
    Spring cloud微服务实战——基于OAUTH2.0统一认证授权的微服务基础架构
    Java8中 Date和LocalDateTime的相互转换
    sonar rule
    图论篇2——最小生成树算法(kurskal算法&prim算法)
    图论篇1——图的基本概念
    数论篇6——欧拉函数
    数论篇5——数论四大定理
    数论篇4——逆元(数论倒数)
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9657177.html
Copyright © 2011-2022 走看看