【四连测】——monkey


Monkey Yang Han

前言+题目描述

加入C2020退役团,享受四连测所有题的快乐!
题目加团可看


这道题怕不是猴子派来的题……

分析

单调队列优化DP……

状态分析

这个状态还是挺好想的……
d p i dp_i dpi表示到第 i i i号的最小花费

DP分析

首先可以写出一个非常暴力的DP转移方程,如下
d p i = m i n { d p j + ( h j &lt; = h i ) } dp_i=min\{dp_j+(h_j&lt;=h_i)\} dpi=min{dpj+(hj<=hi)}
应该看的人都可以理解吧
找一个 j j j,使 i i i j j j的距离最小,显然 d p j + ( h j &lt; = h i ) dp_j+(h_j&lt;=h_i) dpj+(hj<=hi)就是这个距离。
不过,这样做是 O ( n 2 Q ) O(n^2Q) O(n2Q)的,不超时才怪……

单调队列

不会请右转我的这篇博客
对于任意两个决策点 j j j k k k来说,如果 d p j &lt; d p k dp_j&lt;dp_k dpj<dpk,那么转移到 k k k绝不会优于转移到 j j j(到 j j j最多劳累值只加1)
如果 d p j = d p k dp_j=dp_k dpj=dpk,那么直接比较 h i h_i hi h j h_j hj即可得到谁更优
于是,我们就可以用一个单调队列来储存了,就变成了一个滑动窗口(不会同样请看上博客)的问题——距离为输入的 K K K,找单调队列中的最小值即可,小于的定义即为 d p j &lt; d p k   ∥   ( d p j = d p k &amp; &amp; h j &gt; h k ) dp_j&lt;dp_k\ \|\ (dp_j=dp_k\&amp;\&amp;h_j&gt;h_k) dpj<dpk  (dpj=dpk&&hj>hk) j j j更优
时间复杂度为 O ( n Q ) O(nQ) O(nQ) n n n 1 0 6 10^6 106 Q Q Q 25 25 25 2.5 ∗ 1 0 7 2.5*10^7 2.5107还是可以在 2 2 2秒时限下卡过的

代码

#include<map>
#include<set>
#include<list>
#include<queue>
#include<deque>
#include<stack>
#include<ctime>
#include<cmath>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cctype>
#include<string>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<iomanip>
#include<iostream>
#include<algorithm>
using namespace std;
#define reg register
template <typename T>
inline T read() {
    T a=0; char c=getchar(),f=1;
    while(c<'0'||c>'9') {
        if(c=='-') f=-f;
        if(c==-1) return c;
        c=getchar();
    }
    while(c>='0'&&c<='9') a=(a<<1)+(a<<3)+(c^48),c=getchar();
    return a*f;
}
template <class T>
inline int write(T x) {
    if(x<0) x=(~x)+1, putchar('-');
    if(x/10) write(x/10);
    return putchar(x%10|48);
}
template <class T>
inline int write(T x,char c) {
    return write(x)&&putchar(c);
}
template <class T>
inline T Max(T a,T b) { return a>b?a:b; }
template <class T>
inline T Min(T a,T b) { return a<b?a:b; }
template <class T>
inline T Abs(T a) { return a<0?-a:a; }
const int MAXN=1000005;
int n;
int h[MAXN];
int dp[MAXN];
int dq[MAXN],fnt,bck;
inline void init() {
    n=read<int>();
    for(reg int i=1;i<=n;i++)
        h[i]=read<int>();
}
inline bool comp(int a,int b) { //返回a优于b
    return dp[a]<dp[b]||(dp[a]==dp[b]&&h[a]>h[b]);
}
inline void solve() {
    int Q=read<int>();
    while(Q--) {
        memset(dp,0x3f,sizeof dp);
        dp[1]=0;
        fnt=1; bck=0;
        int k=read<int>();
        dq[++bck]=1;
        for(reg int i=2;i<=n;i++) {
            while(fnt<=bck&&dq[fnt]<i-k)
                fnt++;
            dp[i]=dp[dq[fnt]]+(h[i]>=h[dq[fnt]]);
            while(fnt<=bck&&comp(i,dq[bck]))
                bck--;
            dq[++bck]=i;
        }
        write(dp[n],'\n');
    }
}
int main() {
    init();
    solve();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值