📌  相关文章
📜  国际空间研究组织 | ISRO CS 2015 |问题 32(1)

📅  最后修改于: 2023-12-03 15:07:34.320000             🧑  作者: Mango

国际空间研究组织 | ISRO CS 2015 | 问题 32

这是一道关于寻找障碍物数量的问题。假设有一个 $n \times m$ 的矩阵,其中表示地形信息,每个位置的值为 0 或 1。0 表示这个位置是平原,1 表示这个位置有障碍物。现在,你需要找到包含最多障碍物的矩形,输出其中的障碍物数量。

例如,对于以下 $n \times m$ 矩形:

0 0 1 0 1
1 0 1 0 0
0 1 0 1 0
0 0 0 0 1
1 1 0 1 0

包含最多障碍物的矩形是:

0 1 0
1 0 1
0 0 0

其中障碍物数量为 5。

解决方案

这道题可以使用动态规划来解决。考虑对于每个位置 $(i,j)$,如果它是一个平原,那么我们可以向上、向左、向左上三个方向扩展,以得到一个包含该点的最大矩形。具体地,我们定义 $f_{i,j,0/1/2}$ 分别表示向上、向左、向左上扩展时的最大矩形大小。那么:

$$ \begin{aligned} f_{i,j,0} &= \begin{cases} 0, & \text{if }a_{i,j} = 1 \ f_{i-1,j,0}+1, & \text{otherwise} \end{cases} \ f_{i,j,1} &= \begin{cases} 0, & \text{if }a_{i,j} = 1 \ f_{i,j-1,1}+1, & \text{otherwise} \end{cases} \ f_{i,j,2} &= \begin{cases} 0, & \text{if }a_{i,j} = 1 \ \min(f_{i-1,j-1,0},f_{i-1,j,1},f_{i,j-1,2})+1, & \text{otherwise} \end{cases} \end{aligned} $$

注意到这个式子中有一个 $\min$,因此我们可以使用线段树维护。

最后,对于每个位置,我们取三个方向的最小值,然后计算最大值即可。

代码
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1005;

int n,m,a[MAXN][MAXN],ans;
int f[MAXN][MAXN][3];

struct Node {
    int l,r;
    int mn;
}tr[MAXN*4];

void pushup(int p) {
    tr[p].mn = min(min(tr[p*2].mn,tr[p*2+1].mn),tr[p*2].r+tr[p*2+1].l);
}

void build(int p,int l,int r) {
    tr[p] = {l,r,0};
    if(l == r) {
        tr[p].mn = f[l][m][2];
        return;
    }
    int mid = (l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    pushup(p);
}

void update(int p,int x) {
    if(tr[p].l == tr[p].r) {
        tr[p].mn = f[tr[p].l][x][2];
        return;
    }
    int mid = (tr[p].l+tr[p].r)/2;
    if(x <= mid) update(p*2,x);
    else update(p*2+1,x);
    pushup(p);
}

Node query(int p,int l,int r) {
    if(tr[p].l >= l && tr[p].r <= r) return tr[p];
    int mid = (tr[p].l+tr[p].r)/2;
    if(l > mid) return query(p*2+1,l,r);
    if(r <= mid) return query(p*2,l,r);

    Node res = {0,0,0};
    Node L = query(p*2,l,r), R = query(p*2+1,l,r);

    res.l = L.l, res.r = R.r;
    res.mn = min(L.mn,min(R.mn,L.r+R.l));
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> a[i][j];
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) {
            if(!a[i][j]) f[i][j][0] = f[i-1][j][0]+1;
            if(!a[j][i]) f[j][i][1] = f[j][i-1][1]+1;
            if(!a[i][j]) f[i][j][2] = min(f[i-1][j-1][2],min(f[i-1][j][0],f[i][j-1][1]))+1;
        }
    build(1,1,n);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) {
            Node t = query(1,i-f[i][j][0]+1,i);
            ans = max(ans,t.mn);
            update(1,j);
        }
    cout << ans << endl;
    return 0;
}

代码中,我们使用了线段树来维护每个区间的最小值,时间复杂度为 $O(nm\log n)$。