HDU 6957 Maximal submatrix(悬线法 || 优先队列 || 单调栈 )
Posted jpphy0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 6957 Maximal submatrix(悬线法 || 优先队列 || 单调栈 )相关的知识,希望对你有一定的参考价值。
问题
HDU 6957 Maximal submatrix - https://acm.hdu.edu.cn/showproblem.php?pid=6957
概述
悬线法、优先队列、单调栈的共同点是遍历所有可行矩形区域,不漏不重(或者很少重复)
悬线法
分析
-
矩形区域特征量:每个可行的矩形区域有4个特征量,即上、下、左、右4个边界
-
方格特征量:每个方格有3个特征量,即在同列中向上扩展的最大高度,以及在扩展高度最大时向左向右能扩展到的位置(假设不可向下扩展,而且优先向上方扩展,然后向两侧扩展)
-
特征量的关系:矩形区域特征量可以转化为该矩形区域底边上的某个或某几个方格的特征量;
-
对应关系:一个矩形区域可能对应多个特征方格,一个方格仅对应了一个矩形区域;遍历方格就能遍历所有可行矩形区域
-
方格特征量数组: h [ i , j ] , l [ i , j ] , r [ i , j ] h[i,j],l[i,j],r[i,j] h[i,j],l[i,j],r[i,j],即 [ i , j ] [i,j] [i,j] 位置的方格的特征量
-
方格数量 n ∗ m n*m n∗m,每个方格扩展的复杂度是 O ( 1 ) O(1) O(1),因此复杂度 O ( n ∗ m ) O(n*m) O(n∗m)
-
递推(区域合并)关键: h [ i ] [ j ] h[i][j] h[i][j] 根据 h [ i − 1 ] [ j ] , l [ i ] [ j ] h[i-1][j],l[i][j] h[i−1][j],l[i][j] 根据行内左侧的最近递减位置及 l [ i − 1 ] [ j ] , r [ i ] [ j ] l[i-1][j],r[i][j] l[i−1][j],r[i][j] 根据行内右侧的最近递减位置及 r [ i − 1 ] [ j ] r[i-1][j] r[i−1][j]。
- 高度递推: [ i , j ] [i,j] [i,j] 位置要么不可向上扩展,那么 h [ i ] [ j ] = 1 h[i][j] = 1 h[i][j]=1 ;要么可以向上扩展,向上能扩展的最大高度只需要看 [ i − 1 , j ] [i-1,j] [i−1,j] 位置的最大扩展高度即可,不需要遍历上方的位置,此时 h [ i ] [ j ] = h [ i − 1 ] [ j ] + 1 h[i][j] = h[i-1][j]+1 h[i][j]=h[i−1][j]+1 ,因此高度扩展复杂度 O ( 1 ) O(1) O(1)
- 左边界扩展:如果 h [ i ] [ j ] = 1 h[i][j] = 1 h[i][j]=1,那么 l [ i ] [ j ] = 0 l[i][j] = 0 l[i][j]=0;如果 h [ i ] [ j ] > 1 h[i][j] > 1 h[i][j]>1,那么要考虑 l [ i − 1 ] [ j ] l[i-1][j] l[i−1][j]和第 i i i行内的最近左侧递减位置(tmp),因此 l [ i ] [ j ] = m a x ( l [ i − 1 ] [ j ] , t m p ) l[i][j] = max(l[i-1][j], tmp) l[i][j]=max(l[i−1][j],tmp),因此左边界扩展复杂度也是 O ( 1 ) O(1) O(1)。
- 右侧边界扩展:与左侧边界扩展同理,只是应改成 r [ i ] [ j ] = m i n ( r [ i − 1 ] [ j ] , t m p ) r[i][j] = min(r[i-1][j], tmp) r[i][j]=min(r[i−1][j],tmp),tmp是右侧的最近递减位置,因此右边界扩展复杂度也是 O ( 1 ) O(1) O(1)
- 上图中, [ 3 , 12 ] ⇒ [ 4 , 12 ] [3,12] \\Rightarrow [4,12] [3,12]⇒[4,12]的扩展体现了上述 状态转移关系
代码【1636MS】
/* HDU 6957 Maximal submatrix - 悬线法 */
#include<bits/stdc++.h>
using namespace std;
#define MXN 2010
int n, m, ans;
int v[MXN][MXN], h[MXN][MXN], l[MXN][MXN], r[MXN][MXN];
int main(){
int t, tmp;
scanf("%d", &t);
while(t--){
scanf("%d%d", &n, &m);
ans = m; // 单独一行肯定满足要求
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) scanf("%d", v[i]+j);
for(int i = 1; i <= m; ++i) // 第1行初值
l[1][i] = 0, h[1][i] = 1, r[1][i]=m+1;
// left, 从左向右递推,处理高度和左边界
for(int i = 2; i <= n; ++i){
tmp = 0;// 左侧最近不满足"非递减"的位置,初始值0
for(int j = 1; j <= m; ++j){// 判断区域是否可合并分别处理
if(v[i][j] >= v[i-1][j])// 两个区域可合并,处理左边界和高度
l[i][j] = max(l[i-1][j], tmp), h[i][j] = h[i-1][j]+1;
else tmp = j, l[i][j] = 0, h[i][j] = 1;// 两个区域不可合并
}
}
// right,从右向左递推,处理右边界并计算面积
for(int i = 2; i <= n; ++i){
tmp = m+1;// 右侧最近不满足"非递减"的位置,初始值m+1
for(int j = m; j >= 1; --j){ // 判断区域是否可合并分别处理
if(v[i][j] >= v[i-1][j]) r[i][j] = min(r[i-1][j], tmp);
else tmp = j, r[i][j] = m+1;
ans = max(ans, (r[i][j]-l[i][j]-1)*h[i][j]);//求面积的最大
}
}
printf("%d\\n", ans);
}
return 0;
}
优先队列
分析
- 初始状态:
[
1
,
10
]
[1,10]
[1,10] 区间是一个满足条件的区间,如下图1
- [ 2 , 3 ] [2,3] [2,3] 位置上出现一个递减数,将把 [ 1 , m ] [1,m] [1,m] 区间分割成 左、中、右三个区间;
- 若已有区间是按左边界优先的方式排列,那么图2中左和中区间将不再受第2行的后续递减数的影响,而右区间可能还将受到第2行内的递减数影响;当第2行内的递减数全部处理后,将未处理的第2行以前的区间高度+1;
- 然后添加一个高度1的 [ 1 , 10 ] [1,10] [1,10] 区间,表示第2行可以构成一个区间【此区间说明了分割出来的中区间无需加入】
- 然后处理第3行,依次类推
- 区间有序通过优先队列实现,且采用2个优先队列的滚动方式
- 待处理的区间放一个队列(原队列),左区间放另一个队列(新队列),右区间放原队列;处理完一行后,原队列和新队列角色互换。
- 优化:同底的保留高度最大的,同时考虑现有的区域的最大可能值,若无法达到已有的最大值则抛弃
代码【2636MS】
/* HDU 6957 Maximal submatrix - DP+优先队列+剪枝 */
#include<bits/stdc++.h>
using namespace std;
#define MXN 2010
int n, m, v[MXN], ans;
int mt[2][MXN][MXN];
void *ptr;
struct Range{
priority_queue<Range> *q;
int l, r, h, line;
bool operator<(const Range x)const{
if(l == x.l && r == x.r) return h < x.h;
else if(l == x.l) return r > x.r;
else return l > x.l;
}
Range(int line, int l, int r, int h):line(line),l(l),r(r),h(h){
if(ptr != NULL) q = (priority_queue<Range> *)ptr;
}
int area(){ return (r-l+1)*h;}
int mxArea(){ return (r-l+1)*(h+n-line);}// 可能的最大面积
Range left(int x){
return Range(line+1, l, r < x ? r : x-1, x <= l ? -1 : h+1);
}
Range right(int x){
return Range(line, x+1, r, r > x ? 1 : -1);
}
void add(){
if(h <= mt[line&1][l][r]) return;
if(mxArea() <= ans) return;
ans = max(ans, area());
q[line&1].push(*this);
}
void split(int x){
left(x).add(), right(x).add();
}
};
priority_queue<Range> pq[2];
void solve(int x, int cline){
priority_queue<Range> &tq = pq[cline&1];
while(!tq.empty() && tq.top().l <= x){
Range tr = tq.topHDOJ6957Maximal submatrix(单调栈,最大子矩阵面积)