题目链接: A-All-one Matrices.
题目描述
          
\;\;\;\;\;
n
n
n 行
m
m
m 列的
01
01
01 矩阵。
          
\;\;\;\;\;
求:全
1
1
1 子矩阵的个数,且不被其它全
1
1
1 子矩阵包含。
           \;\;\;\;\; 1 ≤ n , m ≤ 3000 1\leq n,m \leq 3000 1≤n,m≤3000.
思路
思路一
      
\;\;\;
预处理
U
p
[
i
]
[
j
]
Up[i][j]
Up[i][j] 表示位置
(
i
,
j
)
(i,j)
(i,j) 向上连续
1
1
1 的个数;
s
u
m
[
i
]
[
j
]
sum[i][j]
sum[i][j] 表示位置
(
i
,
j
)
(i,j)
(i,j) 的行前缀和。
      
\;\;\;
枚举每个位置
(
i
,
j
)
(i,j)
(i,j) 所在行为矩阵的底,向左向右找到高度不小于
U
p
[
i
]
[
j
]
Up[i][j]
Up[i][j] 的位置
(
i
,
L
)
,
(
i
,
R
)
(i,L),(i,R)
(i,L),(i,R),如果下一行
[
L
,
R
]
[L,R]
[L,R] 不全为
1
1
1,即当前矩阵为一个所求矩阵。
判断条件:
s
u
m
[
i
+
1
]
[
R
]
−
s
u
m
[
i
+
1
]
[
L
−
1
]
  
!
=
R
−
L
+
1
sum[i+1][R] - sum[i+1][L-1] \; != R-L+1
sum[i+1][R]−sum[i+1][L−1]!=R−L+1.
      
\;\;\;
但是某些位置可能找到相同的矩阵,所以还要去重。
      
\;\;\;
求位置
(
i
,
j
)
(i,j)
(i,j) 的
L
L
L:从左向右遍历,维护一个单调上升的栈。如果当前位置的高度大于栈顶元素高度,
L
=
j
L=j
L=j,否则,弹栈,直至 当前位置的高度大于栈顶元素高度 或 栈空 ,
L
=
s
.
t
o
p
(
)
+
1
L= s.top()+1
L=s.top()+1;
      
\;\;\;
求位置
(
i
,
j
)
(i,j)
(i,j) 的
R
R
R:操作类似。从右向左遍历,维护一个单调上升的栈。
      
\;\;\;
去重:如果当前位置的高度 在其左边连续
1
1
1中,不曾出现过,说明为一个所求矩阵。操作类似,仍用单调栈即可。
思路二
      
\;\;\;
官方题解思路。
      
\;\;\;
枚举每一行作为矩阵的底边所在行,从前往后枚举每一列,并维护一个关于
U
p
[
i
]
[
j
]
Up[i][j]
Up[i][j] 的单调上升的栈,对于栈中每个高度
h
h
h,还要维护一个其向左最远能拓展的位置
p
o
s
pos
pos.
      
\;\;\;
每当有元素
(
h
,
p
o
s
)
(h,pos)
(h,pos) 退栈的时候,可以得到一个全
1
1
1 矩阵:底为
i
i
i,左右
[
p
o
s
,
j
−
1
]
[pos,j-1]
[pos,j−1],高度
h
h
h. 此时,矩阵向上向左向右均不可扩展,只需判断向下是否可扩展,判断条件同上。
      
\;\;\;
可行性分析:官方题解当然可行,只是我开始没看懂。还是曾爷强无敌。 与思路一的区别之处就在于,每次退栈时,可以确定栈内元素的右边界为
j
−
1
j-1
j−1 ,再判断其是否可以向下扩展即可。对于其左边界,即为入栈时 栈顶元素的左边界,若入栈时栈空,即为当前位置。
代码
思路一代码
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 3005;
char s[MAXN][MAXN];
int Up[MAXN][MAXN], L[MAXN][MAXN], R[MAXN][MAXN], sum[MAXN][MAXN];
stack<int>H;
int main() {
// freopen("in.txt", "r", stdin);
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%s",s[i]+1);
}
memset(Up, 0, sizeof(Up));
for(int j = 1; j <= m; j++) {
for(int i = 1; i <= n; i++){
if(s[i][j] == '1') Up[i][j] = Up[i-1][j] + 1;
sum[i][j] = sum[i][j-1] + s[i][j] - '0';
}
}
for(int i = 1; i <= n; i++) {
while(!H.empty()) H.pop();
H.push(m+1);
for(int j = m; j >= 1; j--) {
while(!H.empty() && Up[i][j] <= Up[i][H.top()]) H.pop();
if(H.empty()) R[i][j] = -1;
else R[i][j] = H.top()-1;
H.push(j);
}
while(!H.empty()) H.pop();
H.push(0);
for(int j = 1; j <= m; j++) {
while(!H.empty() && Up[i][j] <= Up[i][H.top()]) H.pop();
if(H.empty()) L[i][j] = -1;
else L[i][j] = H.top()+1;
H.push(j);
}
}
int ans = 0;
for(int i = 1; i <= n; i++) {
while(!H.empty()) H.pop();
for(int j = 1; j <= m; j++) {
if(s[i][j] == '0') {
while(!H.empty()) H.pop();
continue;
}
int l = L[i][j], r = R[i][j];
if(sum[i+1][r] - sum[i+1][l-1] == r-l+1) continue;
while(!H.empty() && Up[i][j] < H.top()) H.pop();
if(!H.empty() && Up[i][j] == H.top()) continue;
// if(!H.empty()) cout << Up[i][j] << " " << H.top() << endl;
// cout << i << " " << j << " (" << l << "," << r << ")"<< endl;
H.push(Up[i][j]);
ans++;
}
}
printf("%d\n",ans);
return 0;
}
/*
6 6
011110
000111
110001
000011
001010
011011
ans = 10
5 5
10101
01100
11111
10011
00010
*/
思路二代码
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 3005;
char s[MAXN];
int Up[MAXN][MAXN], sum[MAXN][MAXN];
struct node {
int h, pos;
};
stack<node>S;
int main() {
int n, m;
scanf("%d%d", &n, &m);
memset(Up, 0, sizeof(Up));
for(int i = 1; i <= n; i++) {
scanf("%s",s+1);
for(int j = 1; j <= m; j++){
if(s[j] == '1') Up[i][j] = Up[i-1][j] + 1;
sum[i][j] = sum[i][j-1] + s[j] - '0';
}
}
int ans = 0;
for(int i = 1; i <= n; i++) {
while(!S.empty()) S.pop();
for(int j = 1; j <= m + 1; j++) {
int pos_now = j;
while(!S.empty() && Up[i][j] <= S.top().h) {
if(Up[i][j] < S.top().h) {
int l = S.top().pos;
int r = j-1;
if(sum[i+1][r] - sum[i+1][l-1] != r-l+1) {
ans++;
// cout << i << " " << S.top().x << " ("<< l << "," << r << ")" << endl;
}
}
pos_now = S.top().pos;
S.pop();
}
if(Up[i][j] == 0) continue;
S.push(node{Up[i][j],pos_now});
// cout << i << " " << j << " ("<< S.top().pos << "," << ")" << endl;
}
}
printf("%d\n",ans);
return 0;
}
错误点
      
\;\;\;
开始以为只要将找一些不相交的矩阵即可,比如样例:
      
\;\;\;
看起来无比完美,直到对拍时发现了这样一个例子:
      
\;\;\;
我方才意识到,事情不是我想的这样。有些相交的地方可以组成多个所求矩阵。
      
\;\;\;
果然人傻就要多做题。