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

单调队列优化 DP

时间:2024-10-01 23:02:31浏览次数:1  
标签:std 木板 队列 int tail DP 单调

单调队列可以 \(O(n)\) 求出子区间的最值,且顺序上要求区间的左端点和右端点单调不降。

引入

P1886 滑动窗口 /【模板】单调队列

定义一个队列 \(q\),我们让 \(q\) 中的元素满足单调不降。因此当 \(x\) 入队时,从前往后让不在当前范围的元素出队,从后往前将 \(< x\) 的元素全部出队,然后加入 \(x\)。那么答案就是队列第一个元素。时间复杂度为 \(O(n)\)。

#include <bits/stdc++.h>

constexpr int N = 1e6 + 5;

int n, m, a[N];
int q[N], head, tail;

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    std::cin >> a[i];
  }
  head = 0, tail = -1;
  for (int i = 1; i <= n; i++) {
    while (head <= tail && q[head] < i - m + 1) {
      head++;
    }
    while (head <= tail && a[q[tail]] > a[i]) {
      tail--;
    }
    q[++tail] = i;
    if (i >= m) {
      std::cout << a[q[head]] << ' ';
    }
  }
  std::cout << '\n';
  head = 0, tail = -1;
  for (int i = 1; i <= n; i++) {
    while (head <= tail && q[head] < i - m + 1) {
      head++;
    }
    while (head <= tail && a[q[tail]] < a[i]) {
      tail--;
    }
    q[++tail] = i;
    if (i >= m) {
      std::cout << a[q[head]] << ' ';
    }
  }
  std::cout << '\n';
  return 0;
}

例题

AcWing 298. 围栏

按照 \(S\) 进行排序,设 \(f(i, j)\) 表示当前为第 \(i\) 个工匠,粉刷了 \(j\) 块木板(可以存在未刷的木板),则:

  • 不刷任何木板:\(f(i - 1, j)\)。
  • 不刷当前木板:\(f(i, j - 1)\)。
  • 刷连续一段木板,设粉刷 \([k + 1, j]\) 的木板:

\[f(i, j) = \max\limits_{j - L_i \leq k < S_i}\left\{ f(i - 1, k) + (j - k) \times P_i \right\}, j \geq S_i \]

时间复杂度为 \(O(n^2 m)\)。发现第三种情况我们需要 \(O(n)\) 转移,考虑优化。

将式子变形,可以得到:

\[f(i, j) = \max\limits_{j - L_i \leq k < S_i}\left\{ f(i - 1, k) - k \times P_i \right\} + j \times P_i, j \geq S_i \]

可以用单调队列维护 \(f(i - 1, k) - k \times P_i\)。时间复杂度为 \(O(nm)\)。

#include <bits/stdc++.h>

constexpr int N = 105, M = 16005;

int n, m, f[N][M];
int q[M], head, tail;

struct Node {
  int L, P, S;
  bool operator<(const Node &rhs) {
    return S < rhs.S;
  }
} a[N];

int calc(int i, int j) {
  return f[i - 1][j] - a[i].P * j;
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int x, y, z;
    std::cin >> x >> y >> z;
    a[i] = {x, y, z};
  }
  std::sort(a + 1, a + 1 + m);
  for (int i = 1; i <= m; i++) {
    head = 0, tail = -1;
    q[++tail] = 0;
    for (int j = 1; j <= n; j++) {
      f[i][j] = std::max(f[i - 1][j], f[i][j - 1]);
      while (head <= tail && q[head] < j - a[i].L) {
        head++;
      }
      if (j < a[i].S) {
        while (head <= tail && calc(i, q[tail]) < calc(i, j)) {
          tail--;
        }
        q[++tail] = j;
        continue;
      }
      if (head <= tail) {
        f[i][j] = std::max(f[i][j], a[i].P * j + calc(i, q[head]));
      }
    }
  }
  std::cout << f[m][n] << '\n';
  return 0;
}

标签:std,木板,队列,int,tail,DP,单调
From: https://www.cnblogs.com/unino/p/18444240

相关文章

  • 队列实现
    1、数组实现队列#include<stdio.h>#include<stdlib.h>#include<conio.h>#defineMAX10 /*定义队列的大小*/intmain(){ intfront,rear,val,queue[MAX]={0}; charchoice; front=rear=-1; while(rear<MAX-1&&choice!='e') { ......
  • 每日OJ题_牛客_DP2跳台阶_动态规划_C++_Java
    目录牛客_DP2跳台阶_动态规划题目解析C++代码Java代码牛客_DP2跳台阶_动态规划跳台阶_牛客题霸_牛客网题目解析        当前值只和数组的前两个值有关,在往前面的就无关了,所以没必要申请一个数组,直接使用两个变量即可,这样空间复杂度就满足要求了。C++代码......
  • 栈与队列相关知识(二)
    目录Java中栈(Stack)一.常用方法1.push(Eitem)2.pop()3.peek()4.empty()二.常用方法扩展1.search(Objecto)2.clone()3.contains(Objecto)4.size()5.toArray()Java中队列(Queue)一.常用方法(以LinkedList实现Queue为例)1.add(Ee)2.offer(Ee)3.remove()......
  • RabbitMQ死信队列和延迟队列(具体代码演示)
    先理解以下两点:1.延迟队列存储是延时消息,指当消息被发送以后,不让消费者立即拿到消息,而是等待指定时间后,消费者才能拿到消息进行消费。(队列设置过期时间对队列中所有消息生效,如果队列和消息都设置了消息过期时间,会取时间短的)2.入死信队列的三种情况:1.请求被拒绝的消息2.......
  • Leetcode:栈和队列的互相实现
    目录一、用两个队列实现栈题目:分析:代码实现: 二、用两个栈实现队列题目: 分析:代码:总结:核心就在于先进先出和后进先出之间的一个灵活变换,两个栈能够先进先出,而两个队列能够后进先出 一、用两个队列实现栈题目:.-力扣(LeetCode).-备战技术面试?力扣提供海量技术......
  • 1284 海港 队列 模拟
    思路解释 1. 数据结构选择: 使用 queue 来存储每艘船的到达时间和乘客国籍信息。 使用数组 a 来记录每个国籍的乘客数量。 2. 输入处理: 读取船只数量 n。 对于每艘船,读取其到达时间 t 和乘客数量 k,然后读取每个乘客的国籍 x。 3. 统计不同......
  • 9073 关系网络 广搜 队列
    解决思路 广度优先搜索 (BFS):使用BFS从起点 x 开始搜索,找到到达终点 y 的最短路径。 队列:使用队列存储当前节点和步数。 访问标记:使用数组 vis 标记节点是否被访问过,防止重复访问。#include<bits/stdc++.h>#definelllonglongusingnamespacestd;c......
  • 2520 牛的舞蹈表演 二分答案 优先队列
    解决思路 二分查找:使用二分查找来确定舞台的最小大小 K。 检查函数:定义一个检查函数 check(mid),判断在舞台大小为 mid 时,演出是否能在 T_max 时间内完成。 优先队列:使用优先队列模拟舞台上的奶牛,确保每次有奶牛完成表演时,下一头奶牛立即上台。 更新边界:......
  • 9564 Work Scheduling 结构体排序 优先队列 最小堆 贪心
    解决思路 排序:首先将所有工作按照截止时间 D_i 进行排序。 优先队列:使用一个最小堆来存储当前选择的工作的利润。 选择工作:遍历所有工作,如果当前工作的截止时间大于堆的大小,则将该工作加入堆中;否则,如果当前工作的利润大于堆顶的利润,则替换堆顶的工作。#include......
  • 3319 哈夫曼树 优先队列 最小堆
    #include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=1e3+10,inf=0x3f3f3f3f;//优先队列(最小堆),用于存储叶结点的权值priority_queue<int,vector<int>,greater<int>>q;intn,ans,x;intmain(){//读取叶结点的数量......