P7741 [AHOI2007] 石块地板

FBI Warning

此题是一道思维题,请不要"对着题解调试程序"(大雾

前置芝士

一维前缀和(就够了)

最大子段和(P1115

二维数组(能做到蓝题的应该都会吧

思路

注意,这题的 $n$ 和 $m$ 是和其他题反过来的!

这题明显是一个卡了我两天的最大子矩阵和板子题。

有同学就要问了:那为什么是“和”呢?问的是“差最大”啊?

但是这个“差最大”问题也可以转换成“和最大”问题。

黑(的数目) - 白(的数目)最大,即要求黑尽可能多,白尽可能少。

令黑色表示 $1$,白色表示 $-1$,即“$1$ 尽可能多,$-1$ 尽可能少”。

$1$ 尽可能多,所有 $1$ 和 $-1$ 的和就尽可能大。

$-1$ 尽可能少,所有 $1$ 和 $-1$ 的和也尽可能大。

反过来,只要所有 $1$ 和 $-1$ 的和尽可能大,$1$ 就多,$-1$ 就少,(数目的)差也就大。

所以保证矩阵内的 $1$ 和 $-1$ 的和尽可能大即可,差即为 $1$ 和 $-1$ 的和。

是不是恍然大明白?(我猜你没有

那么怎么找出这个“和最大”的子矩阵呢?枚举,但不是直接暴力。

首先,在矩阵上划定一个范围:

枚举这个范围需要 $n^2$ 的复杂度,子矩阵将在这个范围中确定。

将这个范围中每行的和算出来:

$s_i$ 表示第 $i$ 行每个数的和,灰框是一个临时的数组。

每行的和需要 $nm$ 的复杂度,但可以用前缀和优化成 $O(m)$。

那么,这个范围就被抽象成了灰框内的数组。

算这个数组的最大子段和,最大的子段就对应着最大的矩阵。

这样,最大矩阵就找到了,复杂度 $O(n^2m)$。

代码

#include <iostream>
#define qjh(r, x, y) s[r][y] - s[r][x - 1]
using namespace std;
int s[401][401], t[401], m, n, ans;char c;
void get()
{
    for(int i = 1;i <= m;++i)
    {
        t[i] = max(t[i], t[i] + t[i - 1]);
        ans = max(ans, t[i]);
    }
}
int main()
{
    cin >> m >> n;
    for(int i = 1;i <= m;++i)
        for(int j = 1;j <= n;++j)
        {
            cin >> c;
            if(c == '1') s[i][j] = s[i][j - 1] + 1;
            if(c == '0') s[i][j] = s[i][j - 1] - 1;
        }
    for(int i = 1;i <= n;++i)
        for(int j = i;j <= n;++j)
        {
            for(int k = 1;k <= m;++k)
                t[k] = qjh(k, i, j);
            get();
        }
    cout << ans;
    return 0;
}
posted @ 2021-07-31 19:58  5k_sync_closer  阅读(2)  评论(0编辑  收藏  举报  来源