首页 > 其他分享 >单调队列及单调队列优化DP

单调队列及单调队列优化DP

时间:2024-02-06 16:45:28浏览次数:33  
标签:队列 tt int hh 单调 区间 DP

首先是单调队列: 

其实单调队列就是一种队列内的元素有单调性(单调递增或者单调递减)的队列,答案(也就是最优解)就存在队首,而队尾则是最后进队的元素。因为其单调性所以经常会被用来维护区间最值或者降低$DP$的维数已达到降维来减少空间及时间的目的。

类似于滑动窗口等,单调队列具有时序性的储存区间最大值或区间最小值,以下为例题:
P1886 滑动窗口 /【模板】单调队列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P1440 求m区间内的最小值 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

上题均可用滑动窗口模板解决:

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int n,m,hh,tt,a[N],q[N],k;
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    hh=1,tt=0;
    for(int i=1;i<=n;i++){
        while(hh<=tt&&q[hh]<i-k+1) hh++;
        while(hh<=tt&&a[q[tt]]>=a[i]) tt--;
        q[++tt]=i;
        if(i>=k) cout<<a[q[hh]]<<' ';
    }
    cout<<endl;
    hh=1,tt=0;
    for(int i=1;i<=n;i++){
        while(hh<=tt&&q[hh]<i-k+1) hh++;
        while(hh<=tt&&a[q[tt]]<a[i]) tt--;
        q[++tt]=i;
        if(i>=k) cout<<a[q[hh]]<<' ';
    }
    return 0;
}

对于单调队列优化$DP$,在状态转移的时候会发生我们需要转移到一个区间的某一个点上,而在最佳答案的情景下这个点一般为最大值或者最小值,考虑遍历区间复杂度较高,可采用求区间最值方法来确定一个点的位置即可:
P1725 琪露诺 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

对于该题,我们从位置 $0$ 开始,然后在位置 $i$ 时,无非是是从区间 $[i-r,i-l]$ 转移而来,故我们使用单调队列储存 $dp$ 状态在这个区间的最大值即可,区间最大值加上现在位置的贡献即为这个位置状态的贡献,如果从某个位置开始可以直接跳走,即 $i+r>n$ 则从此时开始更新迭代答案即可,这里再解释以下为什么是 $i-l$ 入队,因为我们向前走一步,即 $i变成i+1$, 我们的区间窗口也要滑动,此时新进来的数就是 $i-l$

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=2e6+10;
int n,l,r,q[N],hh=1,tt,dp[N],a[N],res=-1e18;
signed main(){
    cin>>n>>l>>r;
    for(int i=1;i<=2*n;i++) dp[i]=-1e18;
    for(int i=0;i<=n;i++) cin>>a[i];
    for(int i=l;i<=n;i++){
        while(hh<=tt&&q[hh]<i-r) hh++;
        while(hh<=tt&&dp[q[tt]]<dp[i-l]) tt--;
        q[++tt]=i-l,dp[i]=dp[q[hh]]+a[i];
        if(i+r>n) res=max(res,dp[i]);
    }
    cout<<res;
}

P3957 [NOIP2017 普及组] 跳房子 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目要求最小金钱数目,则可以考虑用二分,因为我们可以具有区间灵活度,花费金钱越大灵活度越大,所以最终的答案一定是递增的,故可以使用二分答案

接下来是 $check$ 函数,考虑使用单调队列DP,由于每次进行的步数都不一样,所以此时进行状态转移即可得到最优解,首先确定每次跳跃的区间左端点和右端点,然后进行遍历,设置一个变量 $j$,表示最后一个加入队伍的编号,对于 $i$ 之前的距离,如果 $i和j$ 之间相差的距离满足跳跃区间,那么可以考虑是否加入队列,此时单调队列更新完成,更新 $dp[i]$ 即可,由于游戏随时可以停止,所以要时刻判断

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=2e6+10;
pair<int,int>robot[N];
int n,d,k,dp[N],q[N],sum;
bool check(int g){
    int l,r,hh=1,tt=0,j=0;
    if(g>=d) l=1,r=d+g;
    else l=d-g,r=d+g;
    for(int i=1;i<=n;i++) dp[i]=-2e9,q[i]=0;
    dp[0]=0,q[0]=0;
    for(int i=1;i<=n;i++){
        while(robot[i].first-robot[j].first>=l&&j<i){
            if(dp[j]>-2e9){
                while(hh<=tt&&dp[q[tt]]<=dp[j]) tt--;
                q[++tt]=j;
            }
            j++;
        }
        while(hh<=tt&&robot[i].first-robot[q[hh]].first>r) hh++;
        if(hh<=tt) dp[i]=dp[q[hh]]+robot[i].second;
        if(dp[i]>=k) return true;
    }
    return false;
}
signed main(){
    cin>>n>>d>>k;
    for(int i=1;i<=n;i++){
        int a,b; cin>>a>>b;
        robot[i]={a,b};
    }
    int l=1,r=1e9;
    while(l<r){
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    cout<<(r==1e9?-1:r);
}

 

标签:队列,tt,int,hh,单调,区间,DP
From: https://www.cnblogs.com/o-Sakurajimamai-o/p/18009957

相关文章

  • <学习笔记> DP套DP
    游园会考虑直接设\(F(i,j)\)表示对于兑奖串前\(i\)位匹配奖章串的\(\mathrm{LCS}\)位\(j\)的方案数,但是发现没有办法直接转移,因为对于匹配到奖章串不同位置新加一个字符情况是不一样的。考虑\(\mathrm{LCS}\)的转移,设\(dp(i,j)\)表示兑奖串前\(i\)位与奖章串前\(......
  • 数位DP的一般方法
    数位DP?数位DFS!P2657[SCOI2009]windy数-洛谷|计算机科学教育新生态(luogu.com.cn)不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。windy想知道,在a和b之间,包括a和b,总共有多少个windy数?我们使用DFS解决。数位DFS要设计好状态,考虑好哪些条件会......
  • Nginx配置TCP/UDP流量转发
    #usernobody;worker_processes1;#error_loglogs/error.log;#error_loglogs/error.lognotice;#error_loglogs/error.loginfo;#pidlogs/nginx.pid;events{worker_connections1024;}stream{log_formatmain'$remote_addr[$tim......
  • leetcode--5. 最长回文子串(dp)
    记录23:292024-2-5https://leetcode.cn/problems/longest-palindromic-substring/dp[i][j]s[i,j]之间能否构成回文子串[i,j]之间是否能够构成需要考虑[i+1,j-1]是否构成回文子串且s[i]==s[j]当j-1>=i+1时候说明正好是俩个相邻的字符,此时如果s[i]==s[j]就肯定可......
  • kafka系列(一)【消息队列、Kafka的基本概念、Kafka的工作机制、Kafka可满足的需求、Kafk
    (kafka系列一)一、消息队列1.消息队列的来源在高并发的应用场景中,由于来不及同步处理请求,接收到的请求往往会发生阻塞。例如,大量的插入、更新请求同时到达数据库,这会导致行或表被锁住,最后会因为请求堆积过多而触发“连接数过多的异常”(TooManyConnections)错误。因此,在高......
  • 如何实现一个延迟队列?
    延迟队列是我们日常开发过程中,经常接触并需要使用到的一种技术方案。前些时间在开发业务需求时,我也遇到了一个需要使用到延迟消息队列的需求场景,因此我也在网上调研了一系列不同的延迟队列的实现方案,在此进行了一个总结并且给大家进行分享。延迟队列定义首先,队列这种数据结构相信大......
  • DPDK-22.11.2 [六] RSS receive side scaling 网卡分流机制
    这个的作用就是为了提高性能。当分析网络数据时,可以为网口提供多个接收队列,每个cpu处理一个队列。如果每条队列是独立的,那么就可以很好的并发。这里有两个问题,一个是数据需要平均的分配到每个队列;二是同一组数据需要分配到同一个队列。rss就是这个作用,可以设定以ip进行区分,或......
  • 单调队列
    单调队列是一种内部元素具有单调性的队列,可以解决求“区间内最值”的问题。例:P1886滑动窗口/【模板】单调队列分析:暴力枚举的方式是进行\(n-k\)次循环,每次查找长度为\(k\)区间的最值,这样的算法时间复杂度是\(O(nk)\)的,无法通过这个题目。以下分析以最大值为例,最小值同......
  • 编译安装LAMP环境及wordpress部署
    一、安装背景及任务需求1.LAMP简介LAMP是公认的最常见、最古老的黄金Web技术栈Linux操作系统Apache/Nginxweb服务器作用是将HTTP请求从前端转发到后端应用上Mysql/MariadbMysql是一款数据库管理系统,也就是一个存储数据的工具,用户可以自行对数据库进行增加、删除......
  • SpringBoot中使用Spring自带线程池ThreadPoolTaskExecutor与Java8CompletableFuture实
    场景关于线程池的使用:Java中ExecutorService线程池的使用(Runnable和Callable多线程实现):https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126242904Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例:https://blog.csdn.net/BADAO_......