51nod1158 单调栈 个人的想法以及分析

单调栈,顾名思义就是保持内部元素单调性并且保证FILO的一种数据结构。 单调栈的代码实现没有什么难度,但是使用姿势难以想到。

在51nod1158中描述了这样一个问题: 给定一个 0-1 矩阵, 求这个矩阵最大的全 1 子矩阵的面积。

问题十分好理解。 

现在,我们将这个问题拆分成一些子问题来逐个击破。

首先,为了保证程序操作的规律性与有序性,我们需要把问题等价成一个任意规模下的问题。 比如我们可以将其等价为:

  对于第 i 行(列), 求出 以它为边界的最大全 1 子矩阵面积。

  这样子我们最终所需要的其实是 max(ansi);

接下来解决 i 规模下的问题:

  由于矩阵其实是一种二维结构,对二维结构的处理是十分复杂并且耗时的,所以我们考虑把二维的问题转变为一维的问题。

  这样子我们肯定没有办法直接处理。 由于题目要求的是求出子矩阵面积, 所以我们考虑对于一个点 (i,j), 它周围会有多大的全1矩阵。

  首先我们需要在行和列上对 (i,j)  覆盖范围进行扩展。 那么我们自然而然会想到统计的方法。 即统计出行上扩展的长度,以及在此基础上列扩展的长度。

  扩展行的长度比较容易,我们只需要预处理矩阵。 对于 (i,j) 定义 cnt[i][j] 表示它在行上扩展的长度。 则容易得到递推式 cnt[i][j] = (a[i][j] == 1 ? cnt[i][j-1] + 1 : 0)

  列扩展的难点在与需要依赖与行扩展的结果。 每次所扩展的范围一定是扩展到一个离 ( i , j ) 最远的行扩展不为 0 的位置。 这个很好理解,然后用行扩展乘列扩展就是最终答案。

  那我们现在的问题转化为如何求解列扩展。

  对于位置 ( i , j ) 我们已知它的行扩展为 cnt[i][j] 那么我们考虑以位置 ( i, j ) 为起点向 ( i , j-1 ) 的方向扩展。 在这个方向上与位置 ( i , j ) 相连且行扩展大于 cnt[i][j] 的列号都是可扩展列。

  所以我们此时需要找到所有 j 列之前的列扩展大于 cnt[i][j] 的列, 程序上来讲就是实现一个单调减栈, 每次入栈 ( i , j ) 时必定会先弹出大于cnt[i][j] 的元素。

  我们从 ( i, j ) 开始向左扩展, 再向右扩展, 就得到了最终的结果。

  代码如下:

  

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 510;
 4 int a[maxn][maxn];
 5 int tmp[maxn][maxn];
 6 int up[maxn], down[maxn];
 7 int main(){
 8     int m,n;
 9     cin >> m >> n;
10     for(int i = 1; i <= n; i++){
11         for(int j = 1; j <= m; j++){
12             scanf("%d", &a[i][j]);
13         }
14     }
15     for(int i = 1; i <= n; i++){
16         for(int j = 1; j <= m; j++){
17             if(a[i][j]) tmp[i][j] = tmp[i][j-1] + 1;
18         }
19     }
20     int ans = 0;
21     for(int j = 1; j <= m; j++){
22         for(int i = 1; i <= n; i++){
23             int cur = i-1;
24             for(; cur && tmp[i][j] <= tmp[cur][j]; cur = up[cur]);
25             up[i] = cur;
26         }
27         for(int i = n; i > 0; i--){
28             int cur = i + 1;
29             for(; cur <= n && tmp[i][j] <= tmp[cur][j]; cur = down[cur]);
30             down[i] = cur;
31         }
32         for(int i = 1; i <= n; i++){
33             ans = max(ans, (down[i] - up[i] - 1) * tmp[i][j]);
34         }
35     }
36     cout << ans << endl;
37     return 0;
38 }    
时间: 2024-08-29 05:19:51

51nod1158 单调栈 个人的想法以及分析的相关文章

UVa 1451 (数形结合 单调栈) Average

题意: 给出一个01串,选一个长度至少为L的连续子串,使得串中数字的平均值最大. 分析: 能把这道题想到用数形结合,用斜率表示平均值,我觉得这个想法太“天马行空”了 首先预处理子串的前缀和sum,如果在坐标系中描出(i, sum[i])这些点的话. 所求的平均值就是两点间的斜率了,具体来说,在连续子串[a, b]中,有sum[b]-sum[a-1]个1,长度为b-a+1,所以平均值为(sum[b]-sum[a-1])/(b-a+1) 所以就把问题转化为:求两点横坐标之差至少为L-1,能得到的最大

单调栈以及单调队列

单调栈: 定义: 栈内的元素,按照某种方式排列下(单调递增或者单调递减),如果新入栈的元素破坏了单调性,就弹出栈内元素,直至满足单调性. 作用:单调栈可以找到从左/右遍历第一个比它大/小的元素的位置.时间复杂度为O(N): 实现方式:(以维护单调递增栈为例) 进栈操作:每次进入栈时,先检验栈顶元素和进栈元素的大小,如果小于,那么直接入栈:否则,大于等于进栈元素的出栈,直到栈空或者栈顶元素小于入栈元素. 例如:3 8 2 3 1 初始时刻栈为空,3入栈.......................

CDOJ 1132 酱神赏花 dp+单调栈降低复杂度+滚动数组

酱神赏花 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 262143/262143KB (Java/Others) Submit Status 酱神去杭州赏花. 花展在一条街道上举行,这条街道上有一共有n个节点,自左而右从11到nn编号,11号和nn号是左右两个端点,两个相邻端点之间的距离为11.本次花展一共要展出mm朵花,在第titi时刻,有一朵颜值为bibi的花将在第aiai个节点展出,如果酱神在titi时刻处于第xx个节点,

HDU 4923 Room and Moor (多校第六场C题) 单调栈

Problem Description PM Room defines a sequence A = {A1, A2,..., AN}, each of which is either 0 or 1. In order to beat him, programmer Moor has to construct another sequence B = {B1, B2,... , BN} of the same length, which satisfies that: Input The inp

POJ 3415:后缀数组+单调栈优化

题意很简单,求两个字符串长度大于等于K的子串个数 一开始还是只会暴力..发现n^2根本没法做...看了题解理解了半天才弄出来,太弱了... 思路:把两个字符串连接后做一次后缀数组,求出height 暴力的想法自然是枚举第一个子串的起始位置i和第二个子串的起始位置j,肯定会T的 看了题解才知道有单调栈这种有优化方法.. 将后缀分为A组(起始点为第一个字符串).B组 设符合要求的lcp长度为a,则其对答案的贡献为a-k+1(长度为k~a的都是符合要求的) 一开始这里我也是有疑问的,比如说k=1,aa

HDU 2870 Largest Submatrix (单调栈)

http://acm.hdu.edu.cn/showproblem.php?pid=2870 Largest Submatrix Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1569    Accepted Submission(s): 748 Problem Description Now here is a matrix wit

BZOJ_1628_[Usaco2007_Demo]_City_skyline_(单调栈)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1628 给出\(n\)个距形的影子,问最少是多少个建筑的?(建筑的影子可以重叠). 分析 用单调栈维护一下. 栈内是可能"延续"到当前位置的之前的影子.那么显然比当前位置高的不可能.如果有和当前位置等高的影子,就延续过来,就可以少一个建筑,否则,就向栈里加入当前位置高度的影子. 1 #include <bits/stdc++.h> 2 using namespace st

poj_2559 单调栈

题目大意 给出一个柱形图中柱子的高度,每个柱子的宽度为1,柱子相邻.求出柱形图中可能形成的矩形的最大面积. 题目分析 以每个柱子(高度为h[i])为中心,向两边延展求出以该h[i]为高度的矩形的最大宽度w[i].h[i]*w[i]得到以该柱子为中心的最大矩形面积,遍历一遍之后取最大值即可.     关键在于求出以柱子i为中心的两边延展的矩形最大宽度.先考虑柱子i延伸到左边的最大距离:坐标p不断左移,直到h[p] < h[i],然后i - p即为柱子向左延伸的最大长度.如果直接这么做,复杂度为O(

[USACO2005][POJ3044]City Skyline(贪心+单调栈)

题目:http://poj.org/problem?id=3044 题意:以坐标的形式给出一张图,表示一些楼房的正视图,求出楼房的最少个数. 分析:和小学常做的立方体问题很像,很容易想到一个贪心方法,那就是尽量把矮的楼房放在高的楼房的前面,即连续的等高的一些"X"我们把它视为一座楼房. 具体的做法可以维护一个Y坐标单增的栈,从左到右读入每个坐标,将Y坐标与栈顶坐标比较: 若Y==栈顶坐标,那么接着读下面一个坐标 若Y>栈顶坐标,那么把Y坐标加入栈成为新的栈顶 若Y<栈顶坐标