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

浅谈单调队列优化DP

时间:2023-06-28 18:44:36浏览次数:30  
标签:浅谈 队列 LL ++ int ans -- include DP

对于形如

\[f_i=\max(f_{L≤j≤R}+w_i) \]

的状态转移方程,也就是转移来自之前某个定长区间的最值,我们可以使用单调队列来维护区间最值,从而优化时间复杂度。

烽火传递

我们看到题目可以想到用 \(f_i\) 表示考虑到 \(i\) 这个烽火台,点第 \(i\) 个的合法方案中的最小代价

那么可以想到,点了第 \(i\) 个烽火台,前面 \([i-m,i-1]\) 的区间内至少要点一个,然后我们的状态转移方程就是前面区间最小值加上自己所需的代价

边界 \(f_0=0\)

答案 \(\min(f_{n-m+1 \to n})\)

然后这一道题要求定长区间最小值,我们可以用单调队列优化

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
int n, m, w[N], f[N], q[N], ans = N;

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ )
        cin >> w[i];
    int hh = 0, tt = 0;
    for(int i = 1; i <= n; i ++ )
    {
        if(q[hh] < i - m) hh ++ ;//队头滑出
        f[i] = f[q[hh]] + w[i];
        while(hh <= tt && f[q[tt]] >= f[i]) tt -- ;//将比当前元素更差的扔出去
        q[++ tt] = i;//添加元素
        if(i > n - m) ans = min(ans, f[i]);//更新答案
    }
    cout << ans << endl;
    return 0;
}

修剪草坪

这道题目有两种解法,一种是直接计算(未懂),另一种是一种转化的思路

题目让我们最多连续选 \(k\) 个使得效率最大,我们可以看成在 \(k+1\) 里不选一个,使得不选的效率最小,然后用总效率减去不选的效率就可以得到答案

\(f_i\) 表示考虑到 \(i\) 这个奶牛,不选第 \(i\) 个的的最小不选代价

然后同样从前 \(k + 1\) 个转移

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 1e5 + 10;

LL w[N], q[N], h, t, n, k;

int main()
{
    cin >> n >> k;
    LL sum = 0;
    for(int i = 1; i <= n; i ++ ) 
    {
        cin >> w[i];
        sum += w[i];
    }
    
    for(int i = 1; i <= n; i ++ )
    {
        w[i] += w[q[h]];//这里放在前面是因为我们是k+1个不选一个,如果先把队头弹出答案
        if(q[h] < i - k) h ++ ;
        while(h <= t && w[q[t]] >= w[i]) t -- ;
        q[++ t] = i;
    }
    
    cout << sum - w[q[h]];
    return 0;
}

旅行问题

环形问题我们会想到化环为链 ,那么很容易想到将原数组复制两次,这样原问题就转化成了

在新数组中是否存在长度为 \(n + 1\) 的合法区间

接下来我们考虑顺时针的问题

顺时针

每个点 \(i\) 表示从 \(i\) 点加 \(o_i\) 的油再消耗掉 \(d_i\) 的油所剩的油量,也就是 \(o_i-d_i\)

  1. 更新前缀和 \(s_i\)

  2. 从任意一点 \(i\) 出发,顺时针走一圈,我们要保证在过程中油量始终 \(≥0\) ,也就是在 \([i,i+n-1]\) 中,对任意的 \(j(i≤j≤i+n-1)\) ,都要保证 \(s_j-s_{i-1}≥0\), \(i\) 固定,找 \(s_j\) 的最小值,也就是在区间内找 \(s_j\) 最小值,然后与 \(s_i\) 比较

  3. 这里我们要进行反向遍历,从 \(n\times2\) 到 \(1\) (顺时针需要求出 后面 一段区间中的最值,只有从后往前做才能在处理到当前数的时候,把后面数的信息存下来),由于计算的时候会用到 \(i\) ,所以这里我们就先更新再求值。

逆时针

每个点 \(i\) 表示从 \(i\) 点加 \(o_i\) 的油再消耗掉 \(d_{i-1}\) 的油所剩的油量,也就是 \(o_i-d_{i-1}\) (因为是向后走)

更新 \(s_i\) 这里注意是用 \(s_i+s_{i+1}\) 后缀和,那么以 \(i\) 起点的后缀和为 \(s_j-s_{i+1}\)

在任意情况下都有 \(s_j-s_{i+1} ≥ 0\)

然后正向遍历维护 \(s_j\) 的最小值

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 2e6 + 10;
typedef long long LL;

int n;
LL s[N];//前缀和
int o[N], d[N];//油量,距离
int q[N];
bool ans[N];//每个点是否能走到
int main()
{
   cin >> n;
   for(int i = 1; i <= n; i ++ )
       cin >> o[i] >> d[i];
   for(int i = 1; i <= n; i ++ )
       s[i] = s[i + n] = o[i] - d[i];
   for(int i =1; i <= 2 * n; i ++ )
       s[i] += s[i - 1];
   int h = 1, t = 0;
   for(int i = 2 * n; i >= 1; i -- )
   {
       while(h <= t && s[q[t]] >= s[i]) t -- ;
       q[++ t] = i;
       if(q[h] > i + n - 1) h ++ ;
       if(i <= n && s[q[h]] - s[i - 1] >= 0)
           ans[i] = true;
   }
   
   d[0] = d[n];
   for(int i = n; i; i -- )
       s[i] = s[i + n] = o[i] - d[i - 1];
   for(int i = 2 * n; i; i -- )
       s[i] += s[i + 1];
       
   h = 1, t = 0;
   for(int i = 1; i <= 2 * n; i ++ )
   {
       while(h <= t && s[q[t]] >= s[i]) t -- ;
       q[++ t] = i;
       if(q[h] < i - n + 1) h ++ ;
       if(i > n && s[q[h]] - s[i + 1] >= 0)
           ans[i - n] = true;
   }
   for(int i = 1; i <= n; i ++ )
   {
       if(ans[i])
           puts("TAK");
       else
           puts("NIE");
   }
   return 0;
}

标签:浅谈,队列,LL,++,int,ans,--,include,DP
From: https://www.cnblogs.com/ljfyyds/p/17512283.html

相关文章

  • 基于SpringBoot整合Redisson的延迟队列
    一、需求:     1.订单下单超过30分钟以后,如果还未支付,则自动转为取消支付状态 2.订单收货超过七天以后,如果还未评价,则自动转为好评 3.等类似需求二、实现步骤:    1. 引入redisson依赖<dependency><groupId>org.rediss......
  • Java阻塞队列原理
    阻塞队列,关键字是阻塞,先理解阻塞的含义,在阻塞队列中,线程阻塞有这样的两种情况:1.当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。2.当队列中填满数据的情况下,生产者端的所有线程都会自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。阻塞队列的......
  • 浅谈无线测温系统在高压开关柜中的应用
    罗轩志安科瑞电气股份有限公司上海嘉定201801摘要:高压开关柜是配电系统中重要的组成部分,其主要作用是控制电荷、分配电能和开断电流等,对维持系统的稳定性有一定的保障作用。将无线测温技术应用于高压开关柜,可以实现对其进行实时的动态监测,有助于相关工作人员根据高压开关柜的温度......
  • 浅谈智能照明控制管理系统的功能介绍
    罗轩志安科瑞电气股份有限公司上海嘉定201801摘要:智能照明控制系统较好地实现了智能控制、人性化照明和节能降耗的功能,使其在楼宇控制领域变得越来越重要,越来越受到人们的重视。本文介绍了智能照明控制系统的概念、特点、优势、发展方向等内容,并着重对智能照明控制系统的结构......
  • 浅谈 Kotlin 与 Java 互操作 (上)
    前言浅谈Kotlin与Java互操作(上)Kotlinis100%interoperablewithJavaandAndroidKotlin官网的一句标语,其旨意是表达kotlin的Interoperable-互操作特性互操作就表示Kotlin中可以调用Java的开放接口来访问成员属性和成员方法,同时在Java代码中也百分百兼容Kotlin......
  • docker报错:Error response from daemon: driver failed programming external connect
    重启docker-compose时,nginx服务报错。报错信息:Errorresponsefromdaemon:driverfailedprogrammingexternalconnectivityonendpointlikeshop-nginx(f0a809481f5016e6f7ca6e1ed826b0676d5523b15f2954a2d22c03c12a89567d):Bindfor0.0.0.0:80failed:portisalr......
  • PACM Team (牛客多校) (DP 01背包, 维度较多)
    题目大意:给出n个物品,物品有4个空间值,然后有一个权值问在不超过最大的空间值时,最大的权值  思路:一开始想了很多其他思路没有想出来开始广搜算法,发现dp可以解决(注意看数据范围,是满足的)遇到奇怪的题,就试试dp,特别在数据范围很小的时候 ......
  • 谷粒商城项目篇12_分布式高级篇_购物车功能、消息队列RabbitMQ
    目录购物车模块vo的编写编写interceptor绑定user-key线程共享数据购物车商品的增加添加完成重定向避免刷新页面重复提交购物车商品的增删改查消息队列RabbitMQ场景理解概述docker安装RabbitMQ整合SpringBoot消息确认机制一、购物车模块需求描述在线购物车:登录状态添......
  • 强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟
    强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解1.核心词汇深度确定性策略梯度(deepdeterministicpolicygradient,DDPG):在连续控制领域经典的强化学习算法,是深度Q网络在处定性”表示其输出的是一个确定的动作,......
  • 强化学习从基础到进阶--案例与实践[7.1]:深度确定性策略梯度DDPG算法、双延迟深度确定
    强化学习从基础到进阶--案例与实践[7.1]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解项目实战1、定义算法1.1定义模型!pipuninstall-yparl!pipinstallparlimportparlimportpaddleimportpaddle.nnasnnimportpaddle.nn.functionalasFcl......