单调队列:队列中元素单调递增或递减,可以用双端队列实现(deque),队列的前面和后面都可以入队出队。

单调队列优化dp:

问题引入:

dp[i] = min( a[j] ) ,i-m < j <= i

普通的做法是O(nlogn),但是当n很大是,这个复杂度就不行了,考虑用单调队列优化来达到O(n)。

单调队列优化dp时维护的一般都是两个值{ id(下标),value(值)},且它们都保持单调。

对于这个问题,我们维护一个两个值都单调递增的序列。

查询:队首不断删除,直到队首下标大于等于i - m + 1,队首就是答案。

插入:因为要保证下标单调递增,所以从队尾加入元素a[i],因为又要保证值单调递增,所以我们不断删除队尾大于a[i]的元素,直到队尾小于a[i]或者队列为空,然后在队尾添加a[i]。

为什么我们能直接删除队尾大于a[i]的元素呢?

因为队尾删除的那些元素下标比a[i]小且值比a[i]大,如果这些元素可以是答案,那么a[i]肯定比他们好,所以这些值不会对答案产生贡献,所以直接删除就好了。

最后,每个元素最多入队一次,出队一次,复杂度为O(n)。

PS:只维护下标也可以,因为知道了下标就知道了值(如果你保存下来的话),不过如果n太大(以至于数组存不下)只能两个都维护了

例1:POJ 2823

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<deque>
  5. using namespace std;
  6. #define fi first
  7. #define se second
  8. #define pi acos(-1.0)
  9. #define LL long long
  10. #define mp make_pair
  11. #define pb push_back
  12. #define ls rt<<1, l, m
  13. #define rs rt<<1|1, m+1, r
  14. #define ULL unsigned LL
  15. #define pll pair<LL, LL>
  16. #define pii pair<int, int>
  17. #define mem(a, b) memset(a, b, sizeof(a))
  18. #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  19. #define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
  20. //head
  21.  
  22. const int N = 1e6 + ;
  23. int ans1[N], ans2[N];
  24. int main() {
  25. int n, k, x;
  26. scanf("%d %d", &n, &k);
  27. deque<pii>mn, mx;
  28. for (int i = ; i <= n; i++) {
  29. scanf("%d", &x);
  30. while(!mn.empty() && mn.back().se >= x) mn.pop_back();
  31. mn.pb(mp(i, x));
  32. while(!mn.empty() && mn.front().fi < i-k+) mn.pop_front();
  33. ans1[i] = mn.front().se;
  34. while(!mx.empty() && mx.back().se <= x) mx.pop_back();
  35. mx.pb(mp(i, x));
  36. while(!mx.empty() && mx.front().fi < i-k+) mx.pop_front();
  37. ans2[i] = mx.front().se;
  38. }
  39. for (int i = k; i <= n; i++) printf("%d%c", ans1[i], " \n"[i==n]);
  40. for (int i = k; i <= n; i++) printf("%d%c", ans2[i], " \n"[i==n]);
  41. return ;
  42. }

例2:POJ - 3017

思路:multiset + 单调队列优化

单调队列维护的是下标递增的序列,但以这些下标为下标的数列是单调递减的,对于当前的i和队列中的一个下标tmp,他在队列中的上一下标为_tmp,则区间[_tmp+1,i]中的最大值为a[tmp],即转移方程为

dp[i] = min(dp[_tmp] + a[tmp]),tmp属于单调队列,对于单调队列中的第一个元素tmp,_tmp为第一个使得sum[i] - sum[_tmp] >= m的位置

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<deque>
  5. #include<set>
  6. using namespace std;
  7. #define fi first
  8. #define se second
  9. #define pi acos(-1.0)
  10. #define LL long long
  11. #define mp make_pair
  12. #define pb push_back
  13. #define ls rt<<1, l, m
  14. #define rs rt<<1|1, m+1, r
  15. #define ULL unsigned LL
  16. #define pll pair<LL, LL>
  17. #define pii pair<int, int>
  18. #define mem(a, b) memset(a, b, sizeof(a))
  19. #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  20. #define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
  21. //head
  22.  
  23. const int N = 1e5 + ;
  24. LL sum[N], dp[N];
  25. int a[N];
  26. deque<LL>q;
  27. multiset<LL>s;
  28. int main() {
  29. int n;
  30. LL m;
  31. bool f = false;
  32. scanf("%d %lld", &n, &m);
  33. for (int i = ; i <= n; i++) {
  34. scanf("%d", &a[i]);
  35. if(a[i] > m) f = true;
  36. sum[i] = sum[i-] + a[i];
  37. }
  38. if(f) return *puts("-1");
  39. int p = ;
  40. for (int i = ; i <= n; i++) {
  41. while(sum[i] - sum[p] > m) p++;
  42. while(!q.empty() && a[q.back()] <= a[i]) {
  43. int tmp = q.back();
  44. q.pop_back();
  45. if(!q.empty()) s.erase(dp[q.back()] + a[tmp]);
  46. }
  47. if(!q.empty()) s.insert(dp[q.back()] + a[i]);
  48. q.push_back(i);
  49. while(!q.empty() && sum[i] - sum[q.front()-] > m) {
  50. int tmp = q.front();
  51. q.pop_front();
  52. if(!q.empty()) s.erase(dp[tmp] + a[q.front()]);
  53. }
  54. dp[i] = dp[p] + a[q.front()];
  55. if(s.size() != )dp[i] = min(dp[i], *s.begin());
  56. //cout << dp[i] << endl;
  57. }
  58. printf("%lld\n", dp[n]);
  59. return ;
  60. }

例3: POJ 2373

思路:

dp[0] = 0

dp[i] = min {dp[j] + 1} 2*a <= i-j <= 2*b

喷水半径R为整数,L为偶数,所以洒水机在整数点上

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<deque>
  5. #include<set>
  6. #include<cstring>
  7. using namespace std;
  8. #define fi first
  9. #define se second
  10. #define pi acos(-1.0)
  11. #define LL long long
  12. #define mp make_pair
  13. #define pb push_back
  14. #define ls rt<<1, l, m
  15. #define rs rt<<1|1, m+1, r
  16. #define ULL unsigned LL
  17. #define pll pair<LL, LL>
  18. #define pii pair<int, int>
  19. #define mem(a, b) memset(a, b, sizeof(a))
  20. #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  21. #define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
  22. //head
  23.  
  24. const int N = 1e6 + ;
  25. int dp[N], cnt[N], pre[N], suf[N];
  26. deque<int>q;
  27. int main() {
  28. int n, L, a, b, l, r;
  29. while( ~scanf("%d %d", &n, &L)) {
  30. mem(cnt, );
  31. scanf("%d %d", &a, &b);
  32. a *= ;
  33. b *= ;
  34. for (int i = ; i < n; i++) {
  35. scanf("%d %d", &l, &r);
  36. cnt[l+]++;
  37. cnt[r]--;
  38. }
  39. for (int i = ; i <= L; i++) cnt[i] += cnt[i-];
  40. q.clear();
  41. q.push_back();
  42. for (int i = ; i <= L; i += ) {
  43. if(cnt[i]) continue;
  44. while(!q.empty() && (i-q.front()) > b) {
  45. q.pop_front();
  46. }
  47. if(q.empty() || i - q.front() < a) continue;
  48. dp[i] = dp[q.front()] + ;
  49.  
  50. while(!q.empty() && dp[q.back()] > dp[i]) q.pop_back();
  51. q.push_back(i);
  52. }
  53. if(dp[L])printf("%d\n", dp[L]);
  54. else printf("-1\n");
  55. }
  56. return ;
  57. }

例4: 5429 多重背包

思路: 单调队列优化多重背包

代码:

  1. #pragma GCC optimize(2)
  2. #pragma GCC optimize(3)
  3. #pragma GCC optimize(4)
  4. #include<bits/stdc++.h>
  5. using namespace std;
  6. #define fi first
  7. #define se second
  8. #define pi acos(-1.0)
  9. #define LL long long
  10. //#define mp make_pair
  11. #define pb push_back
  12. #define ls rt<<1, l, m
  13. #define rs rt<<1|1, m+1, r
  14. #define ULL unsigned LL
  15. #define pll pair<LL, LL>
  16. #define pli pair<LL, int>
  17. #define pii pair<int, int>
  18. #define piii pair<pii, int>
  19. #define mem(a, b) memset(a, b, sizeof(a))
  20. #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  21. #define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
  22. //head
  23.  
  24. const int N = 7e3 + ;
  25. int dp[N];
  26. int v[N], w[N], c[N];
  27. int main() {
  28. int n, m;
  29. scanf("%d %d", &n, &m);
  30. for (int i = ; i <= n; i++) scanf("%d %d %d", &v[i], &w[i], &c[i]);
  31. for (int i = ; i <= n; i++) {
  32. for (int p = ; p < v[i]; p++) {
  33. int cnt = c[i];
  34. deque<pii> q;
  35. q.push_back({dp[p] + cnt*w[i], });
  36. for (int tot = ; p + tot*v[i] <= m; tot ++) {
  37. cnt--;
  38. while(!q.empty() && tot - q.front().se > c[i]) q.pop_front();
  39. while(!q.empty() && q.back().fi <= dp[p+tot*v[i]] + cnt*w[i]) q.pop_back();
  40. q.push_back({dp[p+tot*v[i]] + cnt*w[i], tot});
  41. dp[p+tot*v[i]] = q.front().fi + (tot - c[i]) * w[i];
  42. }
  43. }
  44. }
  45. printf("%d\n", dp[m]);
  46. return ;
  47. }

算法笔记--单调队列优化dp的更多相关文章

  1. 「学习笔记」单调队列优化dp

    目录 算法 例题 最大子段和 题意 思路 代码 修剪草坪 题意 思路 代码 瑰丽华尔兹 题意 思路 代码 股票交易 题意 思路 代码 算法 使用单调队列优化dp 废话 对与一些dp的转移方程,我们可以 ...

  2. 【笔记篇】单调队列优化dp学习笔记&&luogu2569_bzoj1855股票交♂易

    DP颂 DP之神 圣洁美丽 算法光芒照大地 我们怀着 崇高敬意 跪倒在DP神殿里 你的复杂 能让蒟蒻 试图入门却放弃 在你光辉 照耀下面 AC真心不容易 dp大概是最经久不衰 亘古不化的算法了吧. 而 ...

  3. 2018.09.10 bzoj1499: [NOI2005]瑰丽华尔兹(单调队列优化dp)

    传送门 单调队列优化dp好题. 这题其实很简单. 我们很容易想到一个O(T∗n∗m)" role="presentation" style="position: ...

  4. 单调队列优化DP,多重背包

    单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...

  5. bzoj1855: [Scoi2010]股票交易--单调队列优化DP

    单调队列优化DP的模板题 不难列出DP方程: 对于买入的情况 由于dp[i][j]=max{dp[i-w-1][k]+k*Ap[i]-j*Ap[i]} AP[i]*j是固定的,在队列中维护dp[i-w ...

  6. hdu3401:单调队列优化dp

    第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次 ...

  7. Parade(单调队列优化dp)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2490 Parade Time Limit: 4000/2000 MS (Java/Others)    ...

  8. BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP

    BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP Description 有一排n棵树,第i棵树的高度是Di. MHY要从第一棵树到第n棵树去找他的妹子玩. 如果MHY在 ...

  9. 【单调队列优化dp】 分组

    [单调队列优化dp] 分组 >>>>题目 [题目] 给定一行n个非负整数,现在你可以选择其中若干个数,但不能有连续k个数被选择.你的任务是使得选出的数字的和最大 [输入格式] ...

随机推荐

  1. UI简述

    UI的全称是user interface,即是用户界面.UI设计是指对软件的人机交互,操作编辑,界面美观的整体设计,从简单的角度来说就是,UI是视觉上的东西,包括logo.软件.网页的按钮.网页导航, ...

  2. UML状态机图【图3】--☆

    UML状态机图 基本概述    状态机图描述的是围绕某一事物状态变化的图.它也是三大流程分析利器之一.它和活动图的区别在于,活动图是描述事物发生的流程,是多个角色参与的,而状态机描述的是事物的状态变化 ...

  3. Wxpython零基础制作计算器

    本文关于Wxpython零基础利用python3.6在pycharm下制作计算器,文章末尾有免费源代码供下载 以后同步更新到博客园和这个网站,www.empirefree.top, 这个网站备案号没有 ...

  4. topcoder srm 545 div1

    problem1 link 这个可以贪心地从前向后构造.假设当前已经的字符串为$S$,对于一个字符$c$来说,设将$c$加到$S$后得到的新串为$S^{'}$.那么如果$X+Y+Z \ge minIn ...

  5. Linux内核中的wake_lock[【转】

    本文转载自:https://blog.csdn.net/wuyb2011/article/details/78542233?locationNum=11&fps=1 #include < ...

  6. Mysql的唯一性索引unique

    目录 唯一性索引unique影响: unique与primary key的区别: 存在唯一键冲突时,避免策略: insert ignore: replace into: insert on dupli ...

  7. 0-1背包dp|波动数列|2014年蓝桥杯A组10-fishers

    标题:波动数列 观察这个数列: 1 3 0 2 -1 1 -2 ... 这个数列中后一项总是比前一项增加2或者减少3. 栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a ...

  8. Spring Boot源码分析

    1.核心: SpringApplication.run(SpringbootdemoApplication.class, args); 内部 2.初始化: new SpringApplication( ...

  9. CF981D Bookshelves

    按位贪心+DP的好题qwq 首先看到题目的要求,统计价值的时候的操作是按位与,就要有按位分别计算的意识 开始没意识到结果想了好久还是看了题解才想到 由于统计价值的方式不是加和,所以可能会出现两个较大的 ...

  10. Rime输入法的配色方案

    致青春 so_young: name: "致青春/So Young" author: "五磅兔 zcunlin@foxmail.com" text_color: ...