题意:给定你n个大楼和他们的高度h[i],当满足以下三种条件中的任意一中时,你可以完成跳跃操作。问最少需要跳几次到达第n个楼
2 =< n <= 3e5,肯定是要找到O(n)的解法,我们考虑dp的状态转移是否可行,在第一种情况下,可以直接转移dp[i] = min(dp[i],dp[i-1]+1)。
再看第二种状态,我们想要得到这样一个区间,其中的数都比两侧的大,我们考虑用一个单调栈递增的栈来维护,假如当前遍历到的位置now,对于栈内每一个大于等于now的位置pre,now这个位置的状态都可以有pre来完成转移。因为单单调递增的栈,假如当前的pre大于等于now,那么pre到now这个开区间内的所有位置肯定都是满足大于now的。然后在维护栈的过程中,我们还要看一下是出现了h[pre] == h[now]的情况,因为假如没有出现,那么最后剩下的栈顶元素依然可以再更新一次当前的now的答案,但是如果出现的话,那么区间内的值全部大于;两侧的条件就不满足了,这是不能再次进行更新。
第三种状态和第二种同理,把栈变成单调递减栈,其余操作一致即可。
最后的到dp[n]即为答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 3e5+7;
#define inf 0x3f3f3f3f
int h[MAXN],dp[MAXN];
stack<int>sta_de;//递减栈
stack<int>sta_in;//递增栈
int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i ++){
scanf("%d",&h[i]);
dp[i] = inf;
}
dp[0] = -1;
sta_de.push(1);
sta_in.push(1);
for(int i = 1;i <= n;i ++){
dp[i] = min(dp[i-1]+1,dp[i]);
int f1 = 0,f2 = 0;
while(sta_in.size() && h[sta_in.top()] >= h[i]){
if(h[sta_in.top()] == h[i])
f1 = 1;
dp[i] = min(dp[i],dp[sta_in.top()] + 1);
sta_in.pop();
}
if(!f1 && sta_in.size())//如果没有出现相等的位置的话,剩下的栈顶也是可以更新 当前i的答案的 别忘了这个可能的更新位置
dp[i] = min(dp[i],dp[sta_in.top()] + 1);
//非严格单调递增的栈,因为到等于的时候 中间的部分才会都大于当前走到的 h[i]
while(sta_de.size() && h[sta_de.top()] <= h[i]){
if(h[sta_de.top()] == h[i])
f2 = 1;
dp[i] = min(dp[i],dp[sta_de.top()] + 1);
sta_de.pop();
}
if(!f2 && sta_de.size())
dp[i] = min(dp[i],dp[sta_de.top()] + 1);
sta_in.push(i);
sta_de.push(i);
}
printf("%d\n",dp[n]);
return 0;
}