Discrete Centrifugal Jumps(单调栈+dp)

https://codeforces.com/contest/1407/problem/D


首先定义dp[i]:当前共有i栋楼表示到第i栋楼的最小步数。

根据条件有如下转移:

1.dp[i]=dp[i-1]+1;

2.i前面有一个下标假设为j,且这个j满足(a[j]>max(a[j+1]......,a[i-1]); dp[i]=dp[j]+1;

3.i前面有一个下标假设为j,且这个j满足(a[j]<min(a[j+1].......a[i-1]);dp[i]=dp[j]+1;

那么找这个j的过程用单调栈优化。

具体是什么意思呢?

比如现在有

h[i]:4 3  2 5  这样的楼高度

  i  :   1 2  3 4

设一个down的单调栈,维护栈内的元素是单调递减的。当遍历到i=4的时候,由于h[4]>h[down.top() =(3) ] ,那么此时down栈的单调递减性质破坏,需要不停down.pop(),使得栈里没有比h[4]更校的。让这个h[4]来当down栈此时的龙头。

于是在pop()的过程中,最开始的down.top=3,但是这个是和i=4相邻的,在转移最开始我们就进行了dp[i]=dp[i-1]+1;所以这里其实无所谓第一个top的转移。但是当第二个down.top()==2的时候,这时候进行dp[i]=min(dp[i],dp[down.top]+1)的转移。如此往复到i=n的时候就维护好了。优化了不停找j的过程。

反之亦然

#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=3e5+100;
typedef long long LL;
LL h[maxn],dp[maxn];
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n;cin>>n;
  for(LL i=1;i<=n;i++){
  	cin>>h[i];
  }
  for(LL i=1;i<=n;i++) dp[i]=i;//初始化最大
  dp[1]=0;
  stack<LL>up,down;
  
  up.push(1);down.push(1);
  for(LL i=2;i<=n;i++){
  	dp[i]=dp[i-1]+1;//第一个转移
	
	while(down.size()&&h[i]>=h[down.top()])
	{
		LL x=h[down.top()];
		down.pop();
		if(h[i]>x&&down.size())
		{
			dp[i]=min(dp[i],dp[down.top()]+1);	
		}	
	} 
	down.push(i);//当前的最高元素入队 
	
	while(up.size()&&h[i]<=h[up.top()])
	{
		LL x=h[up.top()];
		up.pop();
		if(h[i]<x&&up.size())
		{
			dp[i]=min(dp[i],dp[up.top()]+1);
		}
	}
	up.push(i);
  }
  cout<<dp[n]<<endl;
return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/108489862