单调栈求极大子矩阵
题意:极大子矩阵的个数单调栈求子矩阵的个数就不说明了,这里有两点需要注意的。
- 因为求的是极大子矩阵,单调栈只能固定一个底边,求出以这个底边为基础的子矩阵个数,所以我们需要确认的是当前求出来的子矩阵还能不能向下扩展,如果不能才能计数。
解决的方法:假设这个矩阵的左右边界为L和R,那么对于下一行的L和R这个区间范围内是不是全都是1,如果不是的话,说明当前矩阵不能在向下扩展了。先预处理出每一行的前缀和,就能通过差分O(1)判断 - 当前的矩阵的左端和右端要确定好。首先是进栈的情况:只要栈顶比当前矩阵高,就弹出栈,弹出去的矩阵的左端也是当前矩阵的左端。
然后是出栈的情况:在没有任何一个矩阵出栈前,栈内的矩阵都是单调递增的,我们定最高的那个矩阵的右端为R,对于栈内所有矩阵的右端都是R。
要想清楚左端和右端的情况。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 3000+10;
int dp[N][N], c[N][N];
stack <pair<int, int> > sk;
int main(void)
{
//ios::sync_with_stdio(false);
//cin.tie(false); cout.tie(false);
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) scanf("%1d", &dp[i][j]);
memcpy(c, dp, sizeof(dp));
for(int i = 2; i <= n; ++i)
for(int j = 1; j <= m; ++j) if(dp[i][j]) dp[i][j] += dp[i-1][j];
for(int i = 1; i <= n; ++i)
for(int j = 2; j <= m; ++j) c[i][j] += c[i][j-1];
int ans = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m+1; ++j) {
int pos = j;
while(!sk.empty() && dp[i][sk.top().first] > dp[i][j]) {
pair<int, int> t = sk.top(); sk.pop();
pos = t.second;
if(i+1 <= n && c[i+1][j-1]-c[i+1][t.second-1] != j-1-t.second+1) ans++;
else if(i == n) ans++;
}
if(!sk.empty() && dp[i][sk.top().first] == dp[i][j]) sk.top().first = j;
else if(dp[i][j]) sk.push(make_pair(j, pos));
}
}
printf("%d\n", ans);
return 0;
}
浙公网安备 33010602011771号