📅  最后修改于: 2023-12-03 15:07:34.320000             🧑  作者: Mango
这是一道关于寻找障碍物数量的问题。假设有一个 $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)$。