zoukankan      html  css  js  c++  java
  • 单调栈

      单调栈是栈中的元素从栈顶到栈底单调递增或递减。单调栈只能从栈顶部添加或者删除元素,以单调递增栈为例,元素从栈顶到栈底逐渐递增,假设当前元素为e,在入栈时,从栈顶往下找,如果栈顶元素小于当前元素e,则弹出栈顶元素,直到栈顶元素大于等于该元素e,然后将e入栈;出栈时,直接弹出栈顶元素,即可得到栈中当前所有元素的最小值。

      由于每个元素都会入栈和出栈一次,因此平摊下来,时间复杂度为O(n).

    单调栈示例: poj 2559 Largest Rectangle in a Histogram

      题目要求求出由给定的整数数组确定的直方图中,连续的一块最大的矩形面积,其中每个直方列的宽度均为1。

    题目解法:

      将关注点放在直方图的每个列上,由该列的高度确定的一个矩形(从该点的峰点出发,分别向左和向右延伸,得到最大的宽度,从而得到由该高度确定的矩形的面积)。然后进行一次遍历,得到所有点确定的矩形中最大的一个。

       从一点向左(或右)延伸的最大长度(以向左延伸为例),就是从该点出发向左走,如果左边点的高度大于等于该点,则长度加1, 直到遇到的点的高度小于该点的高度则停止,得到最大值。如果使用纯粹的搜索方法,对每个点都做一遍“从该点开始,向左走直到遇到小于它高度的点” 则,复杂度为 O(n^2),仔细分析可以发现: 如果 进行完 Ai的搜索,得到高度小于Ai的点为P,在进行Ai+1的 搜索的时候,如果 h[i+1] > h[i], 则直接得到结果i;如果h[i+1] < h[i],则 从P到i之间的点的高度 肯定大于h[i+1](因为它们大于h[i]),所以只需要从P开始接着往前找即可。 这样的搜索过程,可以简化为在一个单调递减的栈中进行查找,这个过程可以用单调栈来实现。 在实现的时候,考虑求该点向左延伸时,左边最小的点的索引,并且将索引用栈来存储。

    代码实现:注意 long long 的类型,以及 long long result = (right[i] - left[i] + 1)*hist[i]; (在hist 为整数数组时候,需要强制转换为 long long)

     不使用单调栈!

    #include<stdio.h>
    #include<iostream>
    #include<string>
    #include<string.h>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<deque>
    #include<list>
    #include<set>
    #include<set>
    #include<map>
    #include<functional>
    #include<algorithm>
    using namespace std;
    const int kMax = 100005;
    /*
    庞大的数组,从小处入手 =_+, 考虑从直方图中每个位置向左和向右扩展,可以得到的以当前位置处高度为最低高度的序列长度w。
    这样,可以求出从位置i,分别向左向右扩展,保持矩形高度为height[i],得到的矩形面积 为height[i]*w
    最后遍历 i,求出最大值。
    
    对于每个位置i,我们想要知道它最左边的高度比它的高度低的位置,以及最右边的高度比它的高度低的位置。
    最naive的方法是对每个i都向左和向右扫描,找到第一个比height[i] 小的位置j,k。这种方法中会有很多重复计算:
    假设 位置 i > j > k
    现在考虑 k 向左扩展能够得到的最大长度,此时向左扫描到了j,如果 height[j] < height[k],此时扫描可以结束;如果 height[j] >= height[k]
    继续向左扫描,而之前已经计算过了j向左扩展得到的最大长度,假设j左边的位置 i处 height[i] >= height[j], 那么height[i] >= height[k],
    在从j向左扫描的时候,路过了i,现在从k向左扫描,还要再路过i一次!!!! 出现了重复。
    那么,如何消除呢?
    在j向左扫描的时候,获得了j左边第一个height[p]比height[j]小的位置p,那么再从k向左扫描的时候,从j,可以直接跳到p....核心想法就是将每个位置
    的左边第一个比该位置处高度小的那个点记录下来,之后直接跳到那个点。
    
    */
    //height 数组
    int height[kMax];
    
    // left_less_index[i] 表示height数组中从i向左查找,height 第一个比 height[i] 小的位置
    int left_less_index[kMax];
    // right_less_index[i] 表示height数组中从i向右查找,height 第一个比 height[i] 小的位置
    int right_less_index[kMax];
    
    long long int solve(int n){
    	int j;	
    	for (int i = 0; i < n; i++){
    		j = i - 1;
    		while (j >= 0 && height[j] >= height[i]){
    			j = left_less_index[j];			
    		}
    		left_less_index[i] = j;
    	}
    	for (int i = n - 1; i >= 0; i--){
    		j = i + 1;
    		while (j < n && height[j] >= height[i]){
    			j = right_less_index[j];
    		}
    		right_less_index[i] = j;
    	}
    	long long result = 0;
    	for (int i = 0; i < n; i++){
    		long long tmp = ((long long)height[i]) * (right_less_index[i] - left_less_index[i] - 1);
    		result = max(tmp, result);
    	}
    	return result;
    }
    int main(){
    	int n;
    	while (scanf("%d", &n)){
    		if (n == 0)
    			break;
    		for (int i = 0; i < n; i++){
    			scanf("%d", &height[i]);
    		}
    		long long int result = solve(n);
    		printf("%lld
    ", result);
    	}
    	return 0;
    }
    

     使用单调栈!

    /*
    除了用数组 left_less_index, right_less_index 记录下来每个位置i处左边/右边第一个高度比height[i]小的位置之外,还可以使用单调栈。
    栈中存放的是 height数组中的索引【注意,单调栈和单调队列中存放的都是数组元素的索引】。每次计算完位置i,栈顶元素就更新为 i。
    每次计算的时候,都从栈顶向栈底寻找第一个height[index] 比height[i]小的index, 而将中间过程中的那些不满足条件的index都弹出栈。
    */
    //height 数组
    int height[kMax];
    //单调栈,栈中存放height数组的索引,从栈顶到栈底 栈中元素(索引)所在位置的height单调递减
    int mon_stack[kMax];
    //left_width[i] 表示i处的元素可以向左扩展的最大长度,不包括i
    int left_width[kMax];
    //right_width[i] 表示i处的元素可以向右扩展的最大长度,不包括i
    int right_width[kMax];
    
    long long int solve(int n){
    	int top = -1;
    	for (int i = 0; i < n; i++){
    		while (top >= 0 && height[mon_stack[top]] >= height[i]){
    			top--;
    		}
    		left_width[i] = top < 0? i: i - mon_stack[top] - 1;
    		mon_stack[++top] = i;
    	}
    	top = -1;
    	for (int i = n - 1; i >= 0; i--){
    		while (top >= 0 && height[mon_stack[top]] >= height[i]){
    			top--;
    		}
    		right_width[i] = top < 0? n - i - 1: mon_stack[top] - i - 1;
    		mon_stack[++top] = i;
    	}
    	long long result = 0;
    	for (int i = 0; i < n; i++){
    		long long tmp = ((long long)height[i]) * (left_width[i] + right_width[i] + 1);
    		result = max(tmp, result);
    	}
    	return result;
    }
    int main(){
    	int n;
    	while (scanf("%d", &n)){
    		if (n == 0)
    			break;
    		for (int i = 0; i < n; i++){
    			scanf("%d", &height[i]);
    		}
    		long long int result = solve(n);
    		printf("%lld
    ", result);
    	}
    	return 0;
    }
    
  • 相关阅读:
    VUE中全局变量的定义和使用
    Pull Request 工作流——更高效的管理代码
    仓储repository概念
    Mysql存储过程历史表备份
    OpenStack一键安装
    VMware虚拟机设置Win10固定ip
    C#_NPOI_Excel各种设置
    pycharm修改镜像
    C#模拟POST上传文件帮助类(支持https、http)
    Windows安装RabbitMQ
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4550269.html
Copyright © 2011-2022 走看看