UVa LA 3029 City Game 状态拆分,最大子矩阵O(n2) 难度:2

题目

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1030


题意

矩阵,有障碍和普通地面两种子元素,求普通地面连成的子矩阵面积最大值 * 3

思路

如刘书

对于子矩阵长方形来说,其底边上必然有一点,该点向上可以延伸的距离就是子矩阵的长,枚举这一点,设为(i,j)。(i,j)不是障碍是普通地面。

令up[i][j]为其向上能延伸的距离,llen[i][j]为以up[i][j]为长的长方形,左边最早开始于哪里,rlen[i][j]为依此右边最晚结束于哪里。

up[i][j]=(i?up[i - 1][j] + 1)

但要如何更新llen和rlen呢?

考虑上方(i - 1, j),如果(i - 1, j)是障碍,那么up[i][j] = 1, llen[i][j]就是(i, j)左边的障碍横坐标+1,rlen以此类推。

如果(i- 1, j)不是障碍,第一种情况,llen[i][j] = llen[i - 1][j],在这多填出的一层内没有障碍物,另一种情况,llen[i][j]依然是(i, j)左边的障碍横坐标+1。

rlen以此类推。

感想

1. 一开始没有想到枚举(i,j)这根作为面积的长,而只是想到枚举(i,j)作为右下角。这样就还要用费时间的方式确定此时的面积长。

2. 后来没想到可以用上方而不是左方来更新rlen或者llen。没有意识到如果上方是障碍,那么只要看左/右的障碍在哪里即可。

3. 最后本来用的是(i && maze[i - 1][j] != 'R'),不知道哪里错了

代码

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <tuple>
#define LOCAL_DEBUG
using namespace std;
const int MAXN = 1e3 + 4;
char maze[MAXN][MAXN];
int up[MAXN][MAXN];
int llen[MAXN][MAXN];
int rlen[MAXN][MAXN];

int main() {
#ifdef LOCAL_DEBUG
    freopen("C:\Users\Iris\source\repos\ACM\ACM\input.txt", "r", stdin);
    //freopen("C:\Users\Iris\source\repos\ACM\ACM\output.txt", "w", stdout);
#endif // LOCAL_DEBUG
    int T;
    scanf("%d", &T);
    for (int ti = 1; ti <= T; ti++) {
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                char tmp[2];
                scanf("%s", tmp);
                maze[i][j] = tmp[0];
            }
        }

        for (int i = 0; i < n; i++) {
            for (int j = 0, last_impede=-1; j < m; j++) {
                if (maze[i][j] == 'R') {
                    up[i][j] = 0;
                    llen[i][j] = 0;
                    last_impede = j;
                }
                else {
                    up[i][j] = (i ? up[i - 1][j] : 0) + 1;
                    llen[i][j] = max((i ? llen[i - 1][j] : 0), last_impede + 1);
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = m - 1, last_impede = m; j >= 0; j--) {
                if (maze[i][j] == 'R') {
                    rlen[i][j] = m;
                    last_impede = j;
                }
                else {
                    rlen[i][j] = min((i ? rlen[i - 1][j] : m), last_impede - 1);
                    ans = max(ans, up[i][j] * (rlen[i][j] - llen[i][j] + 1));
                }
            }
        }
        printf("%d
", ans * 3);
    }

    return 0;
}
View Code