题意
一条路被划分成了n段,每一段有一个高度,一个人有步幅k,代表他最多可以从第x段一步到第x+k段,当h[x]>h[x+k]时,不消耗体力,否则消耗一点体力,求最后到第n段路时最少消耗的体力,最初在第一段路。
对于 30%的数据,保证 T = 1;
对于另 20% 的数据,保证 N <= 500;
对于 100% 的数据,保证 n< = 1e6, T <= 10,ai在 int 内,0 < K <n。
题解
很容易想到转移方程 f[i]= h[i]<h[j] ? f[j] : f[j]+1 (i-k≤j<i)
怎么优化呢,对于高度不同的路,方程都不一样,该怎么去想。
如果方程一样的话,就是在一个长度为k的区间内找f最小的,那不就是单调栈吗?
但转移方程不同,考虑单调栈对于栈首的弹出只与他是否还属于这个区间有关,只要他还能管这个区间,他就是最优的,所以它与去掉不优解无关。我们去掉不优解是在队尾进行的,且每次比较队尾与当前要插入元素的某个值。
那么考虑什么时候队尾的值一定不比当前元素的值优:当两者f值相同时,如果队尾更矮肯定不优,因为在值相同的情况下,他更可能带来1的代价;
那么队尾元素的f比当前元素的f大呢,队尾也是不优的,或者说可以被代替,因为即使当前元素更矮,它带来了1的代价,得到的答案也是≤队尾得到的答案;
队尾元素的f更小就不能弹掉,和上面相同,即使当前元素更高,队尾元素得到的答案也≤当前元素得到答案。
#include<bits/stdc++.h> using namespace std; const int maxn=1000005; int n,m,a[maxn]; int q[maxn],h,t; int f[maxn]; void nice(){ int k;scanf("%d",&k); h=1;t=0; q[++t]=1; f[1]=0; for(int i=2;i<=n;i++){ while(h<=t&&i-q[h]>k) h++; f[i]= a[i]>=a[q[h]] ? f[q[h]]+1 : f[q[h]]; while(h<=t&&((a[i]>a[q[t]]&&f[i]==f[q[t]])||(f[i]<f[q[t]]))) t--; q[++t]=i; } printf("%d ",f[n]); } int main(){ freopen("extra.in","r",stdin); freopen("extra.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); scanf("%d",&m); while(m--) nice(); } /*9 4 6 3 6 3 7 2 6 5 2 2 5*/