题目名称:
Discrete Centrifugal Jumps
题目大意:
有n个楼房,给出n个楼房的高度,开始位于1号楼,最后需到达n号楼。当满足一下任一条件则可转移:
i + 1 = j;
max(hi+1,…,hj-1)<min(hi,hj);
max(hi,hj)<min(hi+1,…,hj-1);
求到达n号楼需要的最少步数。
思路:
根据条件有如下转移:
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<bits/stdc++.h>
using namespace std;
const int maxn=3e5+50;
int h[maxn],dp[maxn],n,d[maxn],top1,u[maxn],top2;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>h[i];
dp[i]=i;
}
dp[1]=0;
d[++top1]=1,u[++top2]=1;
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+1;
while(h[i]>=h[d[top1]]&&top1){
int x=h[d[top1]];
top1--;
if(h[i]>x&&top1){
dp[i]=min(dp[i],dp[d[top1]]+1);
}
}
d[++top1]=i;
while(h[i]<=h[u[top2]]&&top2){
int x=h[u[top2]];
top2--;
if(h[i]<x&&top2){
dp[i]=min(dp[i],dp[u[top2]]+1);
}
}
u[++top2]=i;
}
cout<<dp[n]<<endl;
return 0;
}