单调队列&单调栈

单调队列&单调栈

在一些问题中,可以使用单调队列或者单调栈优化
时间复杂度一般会被优化为O(n)

单调队列

讲解

单调队列: 队尾可以进队出队,对头可以出队(维护队列的单调性,往往会配合二分进一步降低时间复杂度)

  1. 队尾出队的条件是:队列不空且新元素更优,队中的旧元素队尾出队
  2. 每个元素必然从队尾进队一次
  3. 队头出队的条件:队头元素滑出了串口

队列中存储元素的下标,方便判断队头出队

练习

题目1

LeetCode 239. 滑动窗口最大值

class Solution {
public:
const static int N = 1e5 + 10;
int q[N], h, t = -1;
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ans;
for(int i = 0; i < nums.size(); i ++ ){
// 1. 入,当当前元素大于等于队尾元素时,队列不为空,弹出队尾元素,然后加入当前元素
while(h <= t && nums[i] >= nums[q[t]]) t -- ; // ①
q[ ++ t] = i;
// 2. 移除出窗口的元素 0 1 2 3
if(h <= t && q[h] < i - k + 1) h ++ ; // ② 两条语句交换位置无影响
// 3. 记录答案
if(i >= k - 1) ans.push_back(nums[q[h]]);
}
return ans;
}
};

题目2

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

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N], q[N]; // q数组寸元素下标,方便判断队头元素滑出窗口
int main(){
int n, k;
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
int h = 1, t = 0; // 对头、队尾
for(int i = 1; i <= n; i ++ ){
while(h <= t && a[q[t]] >= a[i]) t -- ; // 队列不为空,队尾元素>=新进窗口的元素,队尾弹出队列
q[ ++ t] = i; // 入队
if(q[h] < i - k + 1) h ++ ; // 如果划出窗口队头出队
if(i >= k) printf("%d ", a[q[h]]);
}
puts("");
h = 1, t = 0; // 对头、队尾
for(int i = 1; i <= n; i ++ ){
while(h <= t && a[q[t]] <= a[i]) t -- ; // 队列不为空,队尾元素>=新进窗口的元素,队尾弹出队列
q[ ++ t] = i; // 入队
if(q[h] < i - k + 1) h ++ ; // 如果划出窗口队头出队
if(i >= k) printf("%d ", a[q[h]]);
}
return 0;
}

模板2

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N], q[N], h, t = -1; // h为队列头部,t为队列尾部,如果t >= h则队列不为空
int main(){
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
for(int i = 0; i < n; i ++ ){
// 如果队列不为空,且头部元素出队列
if(h <= t && q[h] < i - m + 1) h ++ ;
// 如果当前值>=队尾元素,出队列
while(h <= t && a[i] <= a[q[t]]) t -- ;
q[ ++ t] = i;
if(i >= m - 1) printf("%d ", a[q[h]]);
}
puts("");
h = 0, t = -1;
for(int i = 0; i < n; i ++ ){
// 如果队列不为空,且头部元素出队列
if(h <= t && q[h] < i - m + 1) h ++ ;
// 如果当前值>=队尾元素,出队列
while(h <= t && a[i] >= a[q[t]]) t -- ;
q[ ++ t] = i;
if(i >= m - 1) printf("%d ", a[q[h]]);
}
return 0;
}

题目2

LeetCode 53. 最大子数组和
解法1:
单调队列

解法2:
动态规划
解法3:
线段树

class Solution {
public:
struct status {
int sum, s, ls, rs; // 区间总和, 最大子段和, 最大前缀和, 最大后缀和
};
status build(vector<int>& nums, int l, int r) {
if (l == r) return {nums[l], nums[l], nums[l], nums[l]};
int mid = l + r >> 1;
auto L = build(nums, l, mid), R = build(nums, mid + 1, r);
status LR;
LR.sum = L.sum + R.sum;
LR.s = max(max(L.s, R.s), L.rs + R.ls);
LR.ls = max(L.ls, L.sum + R.ls);
LR.rs = max(R.rs, R.sum + L.rs);
return LR;
}
int maxSubArray(vector<int>& nums) {
int n = nums.size();
auto res = build(nums, 0, n - 1);
return res.s;
}
};

题目三

Acwing 6. 多重背包问题 III
解法一

#include<bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
int f[N][N], q[N];
int main(){
int n, m, v, w, s;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++ ){
scanf("%d%d%d", &v, &w, &s);
for(int j = 0; j < v; j ++ ){
int h = 0, t = -1;
for(int k = j; k <= m; k += v){
if(h <= t && q[h] < k - s * v) h ++ ;
if(h <= t) f[i][k] = max(f[i - 1][k], f[i - 1][q[h]] + (k - q[h]) / v * w);
while(h <= t && f[i - 1][k] >= f[i - 1][q[t]] + (k - q[t]) / v * w) t -- ;
q[ ++ t] = k;
}
}
}
printf("%d", f[n][m]);
return 0;
}

为什么不对 ? 因为f[i][j]的更新顺序并不是线性的所以答案是不正确的,而使用复制数组g便满足了线性更新,保证了答案的正确

解法二

#include<bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
int f[N], g[N], q[N];
int main(){
int n, m, v, w, s;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++ ){
memcpy(g, f, sizeof f);
scanf("%d%d%d", &v, &w, &s);
for(int j = 0; j < v; j ++ ){
int h = 0, t = -1;
for(int k = j; k <= m; k += v){
if(h <= t && q[h] < k - s * v) h ++ ;
if(h <= t) f[k] = max(g[k], g[q[h]] + (k - q[h]) / v * w);
while(h <= t && g[k] >= g[q[t]] + (k - q[t]) / v * w) t -- ;
q[ ++ t] = k;
}
}
}
printf("%d", f[m]);
return 0;
}

单调栈

练习

题目1

LeetCode 739. 每日温度
逆序更新

class Solution {
public:
const static int N = 1e5 + 10;
int stk[N], t;
void push(int x){
stk[t ++ ] = x;
}
void pop(){
t -- ;
}
bool empty(){
return t <= 0;
}
int peek(){
return stk[t - 1];
}
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ans(n);
for(int i = n - 1; i >= 0; i -- ){
int t = temperatures[i];
while(!empty() && t >= temperatures[peek()]) pop();
if(!empty()) ans[i] = peek() - i;
push(i);
}
return ans;
}
};

正序更新

class Solution {
public:
const static int N = 1e5 + 10;
int stk[N], t;
void push(int x){
stk[t ++ ] = x;
}
void pop(){
t -- ;
}
bool empty(){
return t <= 0;
}
int peek(){
return stk[t - 1];
}
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ans(n);
for(int i = 0; i < n; i ++ ){
while(!empty() && temperatures[i] > temperatures[peek()]){
int j = peek();
pop();
ans[j] = i - j;
}
push(i);
}
return ans;
}
};

本文作者:爱情丶眨眼而去

本文链接:https://www.cnblogs.com/zshsboke/p/17866110.html

版权声明:本作品采用©️CC BY-NC-SA 4.0许可协议进行许可。

posted @   爱情丶眨眼而去  阅读(57)  评论(0编辑  收藏  举报
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑
点击右上角即可分享
微信分享提示
  1. 1 赤伶 HITA
  2. 2 樱花树下的约定 (DJ-lucky小阳版) 旺仔小乔
  3. 3 踏雪 国风新语,Babystop_山竹
  4. 4 虞兮叹 闻人听書_
  5. 5 广寒宫 花沫
  6. 6 踏山河 七叔(叶泽浩)
  7. 7 破茧 张韶涵
  8. 8 下山 要不要买菜
  9. 9 红昭愿 音阙诗听
  10. 10 渡我不渡她 独孤
  11. 11 海草舞 陈冬霖
  12. 12 纸短情长 (女声版) 林玉冰
  13. 13 把梦照亮 赵小炮
  14. 14 沙漠骆驼 烟火兄弟
  15. 15 赢在江湖 姜鹏
  16. 16 孤勇者 杨宇峰
  17. 17 口是心非 张大帅
  18. 18 赐我 小时姑娘
  19. 19 囍(Chinese Wedding) 葛东琪
踏山河 - 七叔(叶泽浩)
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

踏山河 - 七叔(叶泽浩)

词 Lyrics:祝何

曲 Music:祝何

编曲 Arranger:祝何

吉他 Guitar:潘春宇

混音师 Mixing Engineer:唐瑜

Atmos 混音:刘三斤/苍白/31Studio

合声 Backing vocals:田跃君

制作人 Produced by:田跃君

监制 Executive producer:蒋雪儿 Snow.J/游文雅

音乐统筹 Music co ordination:林宝川

出品 Produced:青风音乐 X 造音行动

OP/SP:青风音乐Cheerful Music

【未经授权不得翻唱或使用】

秋风落日入长河 江南烟雨行舟

乱石穿空 卷起多少的烽火

万里山河都踏过 天下又入谁手

万里山河都踏过 天下又入谁手

分分合合 不过几十载春秋

我在 十面埋伏 四面楚歌的时候

我在 十面埋伏 四面楚歌的时候

把酒与苍天对酌

纵然一去不回 此战又如何

谁见 万箭齐发 星火漫天夜如昼

刀光剑影交错

而我枪出如龙 乾坤撼动 一啸破苍穹

长枪刺破云霞 放下一生牵挂

望着寒月如牙 孤身纵马 生死无话

风卷残骑裂甲 血染万里黄沙

成败笑谈之间 与青史留下

我在 十面埋伏 四面楚歌的时候

我在 十面埋伏 四面楚歌的时候

把酒与苍天对酌

纵然一去不回 此战又如何

谁见 万箭齐发 星火漫天夜如昼

刀光剑影交错

而我枪出如龙 乾坤撼动 一啸破苍穹

长枪刺破云霞 放下一生牵挂

望着寒月如牙 孤身纵马 生死无话

风卷残骑裂甲 血染万里黄沙

成败笑谈之间 与青史留下

长枪刺破云霞 放下一生牵挂

望着寒月如牙 孤身纵马 生死无话

风卷残骑裂甲 血染万里黄沙

笑谈间 谁能留下