• P3572 [POI2014]PTALittle Bird


    题目传送门

    题意

    从左到右有 \(n\) 颗树, 每棵树有高度, 从一颗树跳到一颗比自己矮的树不用花费,否则花费 \(1\)
    在第 \(i\) 颗树时只能跳到 \([i+1, i+k+1]\),问从第一颗树到最后一颗的最小花费, \(O(n)\)

    我其实不大喜欢这题,因为这题的关键是花费为 \(1\),对其他花费不适用。

    不过这题单调队列维护两个单调性这个想法是挺好的!

    先说正解把?实际上就是一个单调队列优化dp, 你从后往前dp,我们设\(f_i\) 表示从第\(i\) 个点到 \(n\) 号点的最小花费, 转移的时候我们直接从范围内答案最小的点中高度最小的进行转移。为什么是对的?因为考虑答案次大的至少比最小的大1, 我们想要从次大转移的原因是这个点可能很矮,不用花费, 但是因为花费为\(1\), 即便答案最小的高度很高,加上\(1\) 之后才刚好等于次大的, 所以直接从答案最小的转移即可。

    比较妙的是, 我们要考虑两个单调性:高度和花费,看似不可以用单调队列维护, 其实是可以的。
    因为实际上两个单调性是没有交集的, 花费不同直接按花费, 只有花费相同时才考虑高度。


    既然都说了这题不够妙, 那就顺便给出我推的一些性质把!

    对于一个点, 一种最优方案时直接跳到可以跳到的点中, 小于它的最远的点。

    今天的题解就摆到这了, 读自证。

    实现

    #include <iostream>
    #include <cstdio>
    #include <queue>
    using namespace std;
    
    int read(){
        int num=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) num=num*10+c-'0', c=getchar();
        return num;
    }
    
    const int N = 1e6+100;
    int n, T, d[N], f[N];
    
    int main(){
        n = read();
        for(int i=1; i<=n; i++) d[i]=read();
        T = read();
        while(T--){
            int k = read();
            deque<int> q; q.push_back(n);
            for(int i=n-1; i; i--){
                while(q.front() > i+k) q.pop_front();
                f[i] = f[q.front()] + (d[q.front()] >= d[i]);
                while(!q.empty() && (f[q.back()] > f[i] || (f[q.back()]==f[i] && d[q.back()]>=d[i])) ) 
                    q.pop_back();
                q.push_back(i);
            }
            printf("%d\n", f[1]);
        }
        return 0;
    }
    
  • 相关阅读:
    rocketmq手工创建topic出错
    rocketmq
    redis基本操作命令key命令+string命令+事务操作命令
    Redis启动常用管理命令
    --环比去年,row_number() over()取最新版本
    二分查找
    使用Python实现的4种快速排序算法
    卷积神经网络的理解
    两个很赞的用法(count函数里的表达式+计算时间间隔)
    MySQL中exists和in的区别及使用场景
  • 原文地址:https://www.cnblogs.com/ltdjcoder/p/16069348.html
Copyright © 2020-2023  润新知