P3625 [APIO2009]采油区域 题解

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

然而码量有点大。

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

即计算复杂度的时候视 O(nm)O(n2)

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

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

在这里插入图片描述

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

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

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

因此这里引入两个辅助数组 Linei,j,Coli,j

  • Linei,j 表示第 i 行到第 j 行之间的最大 k×k 子矩阵元素和。
  • Coli,j 表示第 i 列到第 j 列之间的最大 k×k 子矩阵元素和。

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

下面以 Linei,j 为例。

首先,对于形如 Linei,i+k1 的结果,由于总共只有 k 行,此时我们可以枚举这 k 行里面的子矩阵,至多只有 mk+1 个。

这部分复杂度是 O(n2)

然后对于任意的 Linei,j,如果 ji+1<k,说明此时行数非法,空着就好(无贡献),否则采用区间 DP 方式转移:Linei,j=max(Linei+1,j,Linei,j1)

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


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

在这里插入图片描述

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

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

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

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

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

也就是向下面这张图:

在这里插入图片描述

接下来以预处理 fi,j,0 为例:

还是看图。

在这里插入图片描述

假设右下角的点为 (i,j),我们要算 fi,j,0

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

  • 绿色的是 fi,j1,0
  • 蓝色的是 fi1,j,0
  • 黄色的是 (ik+1,jk+1)(i,j) 这一块矩形的和。

显然答案只能是上述 3 者的最大值,于是我们可以 O(n2) 处理完 fi,j,0

别的同理。

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


最后的时间复杂度是 O(n2)

几个小细节:

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

比如说下面这样:

在这里插入图片描述

此时的正确答案计算式的其中一种为 fi,j,0+fi,j+1,1+Linei+1,n

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

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

代码:

/*
========= Plozia =========
    Author:Plozia
    Problem: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;
}
posted @   Plozia  阅读(83)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示