P3625 [APIO2009]采油区域 题解
这道题是一道很好的二位前缀和问题。
然而码量有点大。
下面规定 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)。
几个小细节:
- 注意在计算 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 的时候是否合法的判断。
- 在转移的时候要注意分割线不能算两次。
比如说下面这样:
此时的正确答案计算式的其中一种为 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。
千万注意分界线不能算两次。
- 注意 n,mn,mn,m 不能弄错。
- 在计算二维前缀和的时候注意不能出现左上角+右下角与左下角+右上角计算两种情况混着用。
换句话说,假设当前矩阵左上,右上,左下,右下分别为 (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) 并且将其当成矩形左上角与右下角 的错误。 - 不能开
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]采油区域 题解相关推荐
- P3625 [APIO2009]采油区域(前缀和)
P3625 [APIO2009]采油区域(前缀和) 前缀和的好题. 考虑将矩形分成三部分有多少情况. 一共只有六种. 然后就是预处理4个角的最大值前缀和值,和从第i行到第j行的最大前缀和值,第i列到第 ...
- [APIO2009]采油区域 题解
这是本蒟蒻的第一篇紫题题解 题目传送门 这是一道比较考细节的题目 经读题可知要求一个矩阵三个边长为 kkk 子矩阵的和的最大值值,因为要求子矩阵,所以我们可以采用二维前缀和. 我们可以发现这个边长为 ...
- 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个子矩阵,且最 ...
- Luogu 3625 [APIO2009]采油区域
想了很久的dp,看了一眼题解之后感觉自己被安排了. 发现从一个矩形中选择三个不相交的正方形一共只有六种取法. 那么我们可以处理出四个值: $f_{i, j}$分别表示以$(i, j)$为右下角,左下角 ...
- 采油区域java_[APIO2009]采油区域
##题面 给出一个$n×m$的矩阵.请在其中选择$3$个互不相交的,大小恰为$k×k$ 的子矩阵,使得子矩阵的权值和最大. $n\leq1500,m\leq1500$ ##解析 这题和CJOJ2501 ...
- acmore|acmore.cc1211采油区域1212会议中心1213抢掠计划APIO2009
采油区域: #include <iostream> #include <algorithm> #include <cstdio> #include <cstd ...
- 蓝桥杯:试题 算法训练 采油区域 矩阵前缀和+动态规划+分治+枚举
资源限制 时间限制:2.0s 内存限制:512.0MB 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井.被拍卖的整块土地为一个矩形区域,被划分为M× ...
- Java实现 蓝桥杯VIP 算法训练 采油区域
算法训练 采油区域 时间限制:2.0s 内存限制:512.0MB 提交此题 查看参考代码 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井.被拍卖的整 ...
- 采油区域 前缀和+DP
算法训练 采油区域 时间限制:2.0s 内存限制:512.0MB 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井. ...
最新文章
- 【BZOJ】1070: [SCOI2007]修车
- html画线需要适应不同屏幕,hr标签不止创建html水平线也可以画圆噢
- 飞桨上线万能转换小工具,教你玩转TensorFlow、Caffe等模型迁移
- Qt Creator 创建优化的3D场景
- 你真的会玩SQL吗?和平大使 内连接、外连接
- 【转】linux命令:ifconfig命令
- 在sharepointwebpart或EventHandler調試的問題
- Linux 命令(14)—— df 命令
- 广东省计算机应用专业综合理论知识,(计算机应用技术专业综合理论考试说明.doc...
- python中oo是什么意思_00.Python学习笔记
- BootLoad中上位机与下位机交互
- 【中科院信工所】22考研经验分享
- Java线程何时放弃CPU时间片
- 4种常见的嵌入式开发系统
- 腾讯战华为:一场「渠道」之争背后,游戏行业变天了
- 不会英语能学java_不会英语可以学java吗 不会英语怎么学java?
- 备战双十一,你清楚积分兑换系统运营吗
- 六大设计原则(SOLID)备忘
- unity-shader 水的效果WaterEffect
- 计算机与艺术联姻阶段,数字媒体艺术概论第二次作业