这道题是一道很好的二位前缀和问题。

然而码量有点大。

下面规定 nnn 表示行,mmm 表示列,n,mn,mn,m 同阶。

即计算复杂度的时候视 O(nm)O(nm)O(nm) 为 O(n2)O(n^2)O(n2)。

首先预处理 sumi,jsum_{i,j}sumi,j​ 表示从 (1,1)(1,1)(1,1) 到 (i,j)(i,j)(i,j) 的和,也就是二维前缀和,这里略过。

考虑将一个矩阵分成 3 块无非 6 种情况,如下:

上面 6 种情况又分为 2 类:

第一类是前面两种全横排与全竖排。

对于这一类情况,我们需要 O(n2)O(n^2)O(n2) 枚举两个分割行,然后 O(1)O(1)O(1) 求出答案。

因此这里引入两个辅助数组 Linei,j,Coli,jLine_{i,j},Col_{i,j}Linei,j​,Coli,j​。

  • Linei,jLine_{i,j}Linei,j​ 表示第 iii 行到第 jjj 行之间的最大 k×kk \times kk×k 子矩阵元素和。
  • Coli,jCol_{i,j}Coli,j​ 表示第 iii 列到第 jjj 列之间的最大 k×kk \times kk×k 子矩阵元素和。

然后考虑如何预处理出这两个数组。

下面以 Linei,jLine_{i,j}Linei,j​ 为例。

首先,对于形如 Linei,i+k−1Line_{i,i+k-1}Linei,i+k−1​ 的结果,由于总共只有 kkk 行,此时我们可以枚举这 kkk 行里面的子矩阵,至多只有 m−k+1m-k+1m−k+1 个。

这部分复杂度是 O(n2)O(n^2)O(n2)。

然后对于任意的 Linei,jLine_{i,j}Linei,j​,如果 j−i+1<kj-i+1<kj−i+1<k,说明此时行数非法,空着就好(无贡献),否则采用区间 DP 方式转移:Linei,j=max⁡(Linei+1,j,Linei,j−1)Line_{i,j}=\max(Line_{i+1,j},Line_{i,j-1})Linei,j​=max(Linei+1,j​,Linei,j−1​)。

处理完 Line,ColLine,ColLine,Col 之后,我们就可以 O(n2)O(n^2)O(n2) 枚举,O(1)O(1)O(1) 算贡献了。


第二类是后面的四种情况,图我搬过来了:

细心观察上图可以发现,这一类情况的图中都有一个交点(即图中的红点)。

因此我们考虑 O(n2)O(n^2)O(n2) 枚举这个交点。

但是这样我们依然要 O(1)O(1)O(1) 算贡献。

因此我们除了前面的 Line,ColLine,ColLine,Col,还要引入一个辅助数组:

fi,j,0/1/2/3f_{i,j,0/1/2/3}fi,j,0/1/2/3​ 表示以 (i,j)(i,j)(i,j) 为分界点,左上/右上/左下/右下 的最大 k×kk \times kk×k 子矩阵元素和。包括 (i,j)(i,j)(i,j) 这个点。

也就是向下面这张图:

接下来以预处理 fi,j,0f_{i,j,0}fi,j,0​ 为例:

还是看图。

假设右下角的点为 (i,j)(i,j)(i,j),我们要算 fi,j,0f_{i,j,0}fi,j,0​。

这个矩形被我分成了 3 类:

  • 绿色的是 fi,j−1,0f_{i,j-1,0}fi,j−1,0​。
  • 蓝色的是 fi−1,j,0f_{i-1,j,0}fi−1,j,0​。
  • 黄色的是 (i−k+1,j−k+1)(i-k+1,j-k+1)(i−k+1,j−k+1) 到 (i,j)(i,j)(i,j) 这一块矩形的和。

显然答案只能是上述 3 者的最大值,于是我们可以 O(n2)O(n^2)O(n2) 处理完 fi,j,0f_{i,j,0}fi,j,0​。

别的同理。

综上,对于第二类情况,我们就可以 O(n2)O(n^2)O(n2) 枚举交点,O(1)O(1)O(1) 计算答案了。


最后的时间复杂度是 O(n2)O(n^2)O(n2)。

几个小细节:

  1. 注意在计算 fi,j,0/1/2/3,Linei,j,Coli,jf_{i,j,0/1/2/3},Line_{i,j},Col_{i,j}fi,j,0/1/2/3​,Linei,j​,Coli,j​ 的时候是否合法的判断。
  2. 在转移的时候要注意分割线不能算两次。

比如说下面这样:

此时的正确答案计算式的其中一种为 fi,j,0+fi,j+1,1+Linei+1,nf_{i,j,0}+f_{i,j+1,1}+Line_{i+1,n}fi,j,0​+fi,j+1,1​+Linei+1,n​。

千万注意分界线不能算两次。

  1. 注意 n,mn,mn,m 不能弄错。
  2. 在计算二维前缀和的时候注意不能出现左上角+右下角与左下角+右上角计算两种情况混着用。
    换句话说,假设当前矩阵左上,右上,左下,右下分别为 (2,2),(2,4),(4,2),(4,4)(2,2),(2,4),(4,2),(4,4)(2,2),(2,4),(4,2),(4,4),不能出现传进去 (4,2),(2,4)(4,2),(2,4)(4,2),(2,4) 并且将其当成矩形左上角与右下角 的错误。
  3. 不能开 long long,否则会 MLE。
    至于答案手算一下就会发现实际上不会炸 int

代码:

/*
========= Plozia =========Author:PloziaProblem:P3625 [APIO2009]采油区域Date:2021/5/14
========= Plozia =========
*/#include <bits/stdc++.h>typedef long long LL;
const int MAXN = 1500 + 10;
int n, m, k, a[MAXN][MAXN], sum[MAXN][MAXN], f[MAXN][MAXN][4], Line[MAXN][MAXN], Col[MAXN][MAXN];int read()
{int sum = 0, fh = 1; char ch = getchar();for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);return sum * fh;
}
int Max(int fir, int sec) { return (fir > sec) ? fir : sec; }
int Min(int fir, int sec) { return (fir < sec) ? fir : sec; }
int Get(int r1, int c1, int r2, int c2)
{if (r1 > r2) std::swap(r1, r2); if (c1 > c2) std::swap(c1, c2);return sum[r2][c2] - sum[r2][c1 - 1] - sum[r1 - 1][c2] + sum[r1 - 1][c1 - 1];
}void init()
{f[k][k][0] = Get(1, 1, k, k);for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j){if (i > k) { f[i][j][0] = Max(f[i][j][0], f[i - 1][j][0]); }if (j > k) { f[i][j][0] = Max(f[i][j][0], f[i][j - 1][0]); }if (i - k + 1 > 0 && j - k + 1 > 0) { f[i][j][0] = Max(f[i][j][0], Get(i - k + 1, j - k + 1, i, j)); }}f[k][m - k + 1][1] = Get(1, m - k + 1, k, m);for (int i = 1; i <= n; ++i)for (int j = m; j >= 1; --j){if (i > k) { f[i][j][1] = Max(f[i][j][1], f[i - 1][j][1]); }if (j < m - k + 1) { f[i][j][1] = Max(f[i][j][1], f[i][j + 1][1]); }if (i - k + 1 > 0 && j + k - 1 <= m) { f[i][j][1] = Max(f[i][j][1], Get(i - k + 1, j, i, j + k - 1)); }}f[n - k + 1][k][2] = Get(n - k + 1, 1, n, k);for (int i = n; i >= 1; --i)for (int j = 1; j <= m; ++j){if (i < n - k + 1) { f[i][j][2] = Max(f[i][j][2], f[i + 1][j][2]); }if (j > k) { f[i][j][2] = Max(f[i][j][2], f[i][j - 1][2]); }if (i + k - 1 <= n && j - k + 1 > 0) { f[i][j][2] = Max(f[i][j][2], Get(i, j - k + 1, i + k - 1, j)); }}f[n - k + 1][m - k + 1][3] = Get(n - k + 1, m - k + 1, n, m);for (int i = n; i >= 1; --i)for (int j = m; j >= 1; --j){if (i < n - k + 1) { f[i][j][3] = Max(f[i][j][3], f[i + 1][j][3]); }if (j < m - k + 1) { f[i][j][3] = Max(f[i][j][3], f[i][j + 1][3]); }if (i + k - 1 <= n && j + k - 1 <= m) { f[i][j][3] = Max(f[i][j][3], Get(i, j, i + k - 1, j + k - 1)); }}
}int main()
{n = read(), m = read(), k = read();for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)a[i][j] = read();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] + a[i][j];init();for (int i = 1; i + k - 1 <= n; ++i){for (int j = 1; j <= m; ++j){if (j >= k) { Line[i][i + k - 1] = Max(Line[i][i + k - 1], Get(i, j - k + 1, i + k - 1, j)); }if (j + k - 1 <= m) { Line[i][i + k - 1] = Max(Line[i][i + k - 1], Get(i, j, i + k - 1, j + k - 1)); }}}for (int len = k + 1; len <= n; ++len){for (int i = 1; i <= n; ++i){int j = i + len - 1; if (j > n) break ;Line[i][j] = Max(Line[i + 1][j], Line[i][j - 1]);}}for (int j = 1; j + k - 1 <= m; ++j){for (int i = 1; i <= n; ++i){if (i >= k) { Col[j][j + k - 1] = Max(Col[j][j + k - 1], Get(i - k + 1, j, i, j + k - 1)); }if (i + k - 1 <= n) { Col[j][j + k - 1] = Max(Col[j][j + k - 1], Get(i, j, i + k - 1, j + k - 1)); }}}for (int len = k + 1; len <= m; ++len){for (int i = 1; i <= m; ++i){int j = i + len - 1; if (j > m) break ;Col[i][j] = Max(Col[i + 1][j], Col[i][j - 1]);}}int ans = 0;for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j){ans = Max(ans, Col[1][j - 1] + f[i][j][1] + f[i + 1][j][3]);ans = Max(ans, Col[j + 1][m] + f[i][j][0] + f[i + 1][j][2]);ans = Max(ans, f[i][j][0] + f[i][j + 1][1] + Line[i + 1][n]);ans = Max(ans, f[i][j][2] + f[i][j + 1][3] + Line[1][i - 1]);}for (int i = k; i <= n - k + 1; ++i)for (int j = i + k - 1; j <= n - k + 1; ++j)ans = Max(ans, Line[1][i] + Line[i + 1][j - 1] + Line[j][n]);for (int i = k; i <= m - k + 1; ++i)for (int j = i + k - 1; j <= m - k + 1; ++j)ans = Max(ans, Col[1][i] + Col[i + 1][j - 1] + Col[j][m]);printf("%d\n", ans); return 0;
}

P3625 [APIO2009]采油区域 题解相关推荐

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

    P3625 [APIO2009]采油区域(前缀和) 前缀和的好题. 考虑将矩形分成三部分有多少情况. 一共只有六种. 然后就是预处理4个角的最大值前缀和值,和从第i行到第j行的最大前缀和值,第i列到第 ...

  2. [APIO2009]采油区域 题解

    这是本蒟蒻的第一篇紫题题解 题目传送门 这是一道比较考细节的题目 经读题可知要求一个矩阵三个边长为 kkk 子矩阵的和的最大值值,因为要求子矩阵,所以我们可以采用二维前缀和. 我们可以发现这个边长为 ...

  3. luoguP3625 APIO2009 采油区域

    题面 题面 题解 套路题. 我第一想法是设f[i][j][k]f[i][j][k]f[i][j][k]为(1,1)−(i,j)(1,1)-(i,j)(1,1)−(i,j)的矩阵里,选了k个子矩阵,且最 ...

  4. Luogu 3625 [APIO2009]采油区域

    想了很久的dp,看了一眼题解之后感觉自己被安排了. 发现从一个矩形中选择三个不相交的正方形一共只有六种取法. 那么我们可以处理出四个值: $f_{i, j}$分别表示以$(i, j)$为右下角,左下角 ...

  5. 采油区域java_[APIO2009]采油区域

    ##题面 给出一个$n×m$的矩阵.请在其中选择$3$个互不相交的,大小恰为$k×k$ 的子矩阵,使得子矩阵的权值和最大. $n\leq1500,m\leq1500$ ##解析 这题和CJOJ2501 ...

  6. acmore|acmore.cc1211采油区域1212会议中心1213抢掠计划APIO2009

    采油区域: #include <iostream> #include <algorithm> #include <cstdio> #include <cstd ...

  7. 蓝桥杯:试题 算法训练 采油区域 矩阵前缀和+动态规划+分治+枚举

    资源限制 时间限制:2.0s 内存限制:512.0MB 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井.被拍卖的整块土地为一个矩形区域,被划分为M× ...

  8. Java实现 蓝桥杯VIP 算法训练 采油区域

    算法训练 采油区域 时间限制:2.0s 内存限制:512.0MB 提交此题 查看参考代码 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井.被拍卖的整 ...

  9. 采油区域 前缀和+DP

    算法训练  采油区域     时间限制:2.0s     内存限制:512.0MB         采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井. ...

最新文章

  1. 【BZOJ】1070: [SCOI2007]修车
  2. html画线需要适应不同屏幕,hr标签不止创建html水平线也可以画圆噢
  3. 飞桨上线万能转换小工具,教你玩转TensorFlow、Caffe等模型迁移
  4. Qt Creator 创建优化的3D场景
  5. 你真的会玩SQL吗?和平大使 内连接、外连接
  6. 【转】linux命令:ifconfig命令
  7. 在sharepointwebpart或EventHandler調試的問題
  8. Linux 命令(14)—— df 命令
  9. 广东省计算机应用专业综合理论知识,(计算机应用技术专业综合理论考试说明.doc...
  10. python中oo是什么意思_00.Python学习笔记
  11. BootLoad中上位机与下位机交互
  12. 【中科院信工所】22考研经验分享
  13. Java线程何时放弃CPU时间片
  14. 4种常见的嵌入式开发系统
  15. 腾讯战华为:一场「渠道」之争背后,游戏行业变天了
  16. 不会英语能学java_不会英语可以学java吗 不会英语怎么学java?
  17. 备战双十一,你清楚积分兑换系统运营吗
  18. 六大设计原则(SOLID)备忘
  19. unity-shader 水的效果WaterEffect
  20. 计算机与艺术联姻阶段,数字媒体艺术概论第二次作业

热门文章

  1. 一位自由职业者的日常
  2. 为什么程序员适合做自由职业者
  3. Eclipse各版本下载地址
  4. yoyo-rebecca
  5. C#编写数据分析软件(附源码)
  6. Lidar-camera calibration单线激光雷达与相机外参标定
  7. 以小饭桌网站为例介绍抓取动态网页的数据【python爬虫入门进阶】(12)
  8. Kafka之分区副本与ISR
  9. 物联网之boa服务器
  10. metaspolit提示Exploit failed: You must select a target.