单调栈——淹没木板

这是一道单调栈的题,不过网上好像找不到评测系统去提交本题;

问题描述

地上从左到右竖立着 n 块木板,从 1 n依次编号,如下图所示。我们知道每块木板的高度,在第 n块木板右侧竖立着一块高度无限大的木板,现对每块木板依次做如下的操作:对于第 i块木板,我们从其右侧开始倒水,直到水的高度等于第 i块木板的高度,倒入的水会淹没 ai块木板(如果木板左右两侧水的高度大于等于木板高度即视为木板被淹没),求 n次操作后,所有 ai 的和是多少。

PS:ai表示的是这块木板最多可以淹没右边的多少块木板;


考虑如下情况(上图):因为我们只用考虑能够在右侧淹没的木板有多少假设木板的高度从某木板 x 开始如果向右边是单调递减的,那么后继的所有木板都可以被木板 x 淹没,并且高度单调递减一直到某一个木板 y 的高度大于它的前一个木板 y-1 ——


对于木板y,有且仅有两种情况:

1、它不能够被x木板淹没,即它的高度是大于x木板的:


那么既然x木板不能淹没y木板,由于高度从第 x 木板到第 y-1 个木板单调递减的,那么区间[ x , y-1 ]个木板一定也不能淹没y木板,对吧?那么这些木板的使命就已经结束了,简单地说,我们就不必去考虑这些木板了,对吧?


2、它能够被x木板淹没,但是它只能够淹没部分木板;


我们怎么知道它到底有哪些木板能够被淹没呢?由于它的高度呈单调递减状态,可以考虑用单调栈做。为什么呢?我们知道栈是一个 LIFO (Last In First Out)的数据结构,所以单调栈一定也满足栈的全部性质,对吧?只不过单调栈中的元素满足了从栈底到栈顶的单调性而已。而这个单调性正是我们想要的!为什么呢?由于y无法构成另一堵高墙去淹没前面的所有木板,为了确定到底哪块木板 i 才是y木板最终能够使第 i-1 块木板淹没的,我们就可以每次取栈顶去比较:到底是栈顶的木板高度高一些,还是我现在这块木板高一些;如果——

       (i)height [ x ] >= height [ s.top( ) ] :那么栈顶这块木板一定是不可能淹没第 y 块木板后面的第 y+1,y+2 ……木板                                                                        的了,使命已经完结咯,弹栈,seeya!

        ( ii ) height [ x ] < height [ s.top( ) ]:由于我们现在维护的单调栈是从栈底向上单调递减的,所以继续往下走还是一样                                                                     的结果height [ x ] < height [ s.top( ) ],所以这个栈顶一定是我能够使第 i-1 块                                                                    木板淹没的前一块了。所以栈顶这一块一定还可能淹没第 y 块木板后面的木板,                                                                     使命未完,不出栈!

我们保证栈中的元素满足单调性,而且一定是有可以淹没后面的木板的可能性的!

	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x;
		node tmp;
		tmp.id=i;tmp.h=x;
		while((!s.empty()) && s.top().h<=x){
			ans+=i-s.top().id-1;
			s.pop();
		}
		s.push(tmp);
	}


完了吗?

没有——

假如最后几块木板——>……n-2,n-1,n块木板满足单调递减的话,按照刚才的程序来判断的话,你就会发现没有 n+1 这根木板去使所有的木板都结束使命,并求出ai的值!没有关系啊,我们只需要在最后再想象一块长度为无穷大的木板就好了!不过再建一块长度为无穷大的木板相对太麻烦了~,所以干脆不管了,直接出栈就是了(就算你建了无穷大这块板子,你的判断条件条件——>height [ x ] >||<=height [ i ],已经默认为满足了——木板长度没有比无穷大更大的了吧,所以干脆就不写了)

	while(!s.empty()){
		ans+=n+1-s.top().id-1;
		s.pop();
	}
就是这个意思~

下面是完全代码:

#include<bits/stdc++.h>
using namespace std;
const int N=10000+5;
struct node{
	int h,id;
}a[N];
stack<node> s;
int n,ans,x; 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x;
		node tmp;
		tmp.id=i;tmp.h=x;
		while((!s.empty()) && s.top().h<=x){
			ans+=i-s.top().id-1;
			s.pop();
		}
		s.push(tmp);
	}
	while(!s.empty()){
		ans+=n+1-s.top().id-1;
		s.pop();
	}
	cout<<ans;
	return 0;
}

ok;上台讲了这题,恶心啊,讲之前吃了把鸡,没讲好。Jerry Wang啊,别再管情感了,过往是属于过往的了啊……(本蒟蒻命途多舛啊)

继续努力吧!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值