Description
给定一个序列,每次可以从 \(i\) 跳到 \(j (>i)\) 当且仅当满足以下三个条件之一:
-
\(i+1=j\)
-
\(\max(h[i+1,j-1])<\min(h_i,h_j)\)
-
\(\min(h[i+1,j-1])>\max(h_i,h_j)\)
求从下标为 \(1\) 的位置跳到下标为 \(n\) 的位置花费的最小步数。
Solution
考虑 dp,设 \(f_i\) 表示跳到第 \(i\) 个格子的最小步数,那么很显然 \(f_i\) 序列一定是单调的,这是由第一个转移条件保证的。
先考虑第二个条件,分析 \(i\) 状态可以从哪些状态转移过来。
维护一个单调递减的栈,那么有且仅有加入 \(i\) 的弹栈过程中经历的所有栈顶是合法的 \(j\)。
所以我们只需要在加入 \(i\) 的过程中,对每次弹栈后的栈顶 \(j\),做一次 \(j \to i\) 的转移即可。
对第三个条件也是同理的。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
int n;
int a[N],f[N];
int staAsc[N],topAsc,staDec[N],topDec;
signed main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
f[0]=1e9;
f[1]=0;
staAsc[++topAsc]=1;
staDec[++topDec]=1;
for(int i=2;i<=n;i++)
{
f[i]=f[i-1]+1;
while(topAsc && a[staAsc[topAsc]]>=a[i])
{
--topAsc;
if(a[staAsc[topAsc+1]]==a[i]) continue;
f[i]=min(f[i], f[staAsc[topAsc]]+1);
}
staAsc[++topAsc]=i;
while(topDec && a[staDec[topDec]]<=a[i])
{
--topDec;
if(a[staDec[topDec+1]]==a[i]) continue;
f[i]=min(f[i], f[staDec[topDec]]+1);
}
staDec[++topDec]=i;
}
cout<<f[n]<<endl;
return 0;
}