单调队列:队列中元素单调递增或递减,可以用双端队列实现(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

代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<deque>
using namespace std;
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pii pair<int, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head const int N = 1e6 + ;
int ans1[N], ans2[N];
int main() {
int n, k, x;
scanf("%d %d", &n, &k);
deque<pii>mn, mx;
for (int i = ; i <= n; i++) {
scanf("%d", &x);
while(!mn.empty() && mn.back().se >= x) mn.pop_back();
mn.pb(mp(i, x));
while(!mn.empty() && mn.front().fi < i-k+) mn.pop_front();
ans1[i] = mn.front().se;
while(!mx.empty() && mx.back().se <= x) mx.pop_back();
mx.pb(mp(i, x));
while(!mx.empty() && mx.front().fi < i-k+) mx.pop_front();
ans2[i] = mx.front().se;
}
for (int i = k; i <= n; i++) printf("%d%c", ans1[i], " \n"[i==n]);
for (int i = k; i <= n; i++) printf("%d%c", ans2[i], " \n"[i==n]);
return ;
}

例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的位置

#include<iostream>
#include<cstdio>
#include<queue>
#include<deque>
#include<set>
using namespace std;
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pii pair<int, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head const int N = 1e5 + ;
LL sum[N], dp[N];
int a[N];
deque<LL>q;
multiset<LL>s;
int main() {
int n;
LL m;
bool f = false;
scanf("%d %lld", &n, &m);
for (int i = ; i <= n; i++) {
scanf("%d", &a[i]);
if(a[i] > m) f = true;
sum[i] = sum[i-] + a[i];
}
if(f) return *puts("-1");
int p = ;
for (int i = ; i <= n; i++) {
while(sum[i] - sum[p] > m) p++;
while(!q.empty() && a[q.back()] <= a[i]) {
int tmp = q.back();
q.pop_back();
if(!q.empty()) s.erase(dp[q.back()] + a[tmp]);
}
if(!q.empty()) s.insert(dp[q.back()] + a[i]);
q.push_back(i);
while(!q.empty() && sum[i] - sum[q.front()-] > m) {
int tmp = q.front();
q.pop_front();
if(!q.empty()) s.erase(dp[tmp] + a[q.front()]);
}
dp[i] = dp[p] + a[q.front()];
if(s.size() != )dp[i] = min(dp[i], *s.begin());
//cout << dp[i] << endl;
}
printf("%lld\n", dp[n]);
return ;
}

例3: POJ 2373

思路:

dp[0] = 0

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

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

#include<iostream>
#include<cstdio>
#include<queue>
#include<deque>
#include<set>
#include<cstring>
using namespace std;
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pii pair<int, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head const int N = 1e6 + ;
int dp[N], cnt[N], pre[N], suf[N];
deque<int>q;
int main() {
int n, L, a, b, l, r;
while( ~scanf("%d %d", &n, &L)) {
mem(cnt, );
scanf("%d %d", &a, &b);
a *= ;
b *= ;
for (int i = ; i < n; i++) {
scanf("%d %d", &l, &r);
cnt[l+]++;
cnt[r]--;
}
for (int i = ; i <= L; i++) cnt[i] += cnt[i-];
q.clear();
q.push_back();
for (int i = ; i <= L; i += ) {
if(cnt[i]) continue;
while(!q.empty() && (i-q.front()) > b) {
q.pop_front();
}
if(q.empty() || i - q.front() < a) continue;
dp[i] = dp[q.front()] + ; while(!q.empty() && dp[q.back()] > dp[i]) q.pop_back();
q.push_back(i);
}
if(dp[L])printf("%d\n", dp[L]);
else printf("-1\n");
}
return ;
}

例4: 5429 多重背包

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

代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head const int N = 7e3 + ;
int dp[N];
int v[N], w[N], c[N];
int main() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = ; i <= n; i++) scanf("%d %d %d", &v[i], &w[i], &c[i]);
for (int i = ; i <= n; i++) {
for (int p = ; p < v[i]; p++) {
int cnt = c[i];
deque<pii> q;
q.push_back({dp[p] + cnt*w[i], });
for (int tot = ; p + tot*v[i] <= m; tot ++) {
cnt--;
while(!q.empty() && tot - q.front().se > c[i]) q.pop_front();
while(!q.empty() && q.back().fi <= dp[p+tot*v[i]] + cnt*w[i]) q.pop_back();
q.push_back({dp[p+tot*v[i]] + cnt*w[i], tot});
dp[p+tot*v[i]] = q.front().fi + (tot - c[i]) * w[i];
}
}
}
printf("%d\n", dp[m]);
return ;
}

算法笔记--单调队列优化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. django创建ORM模型、通过ORM模型操作单个表、ORM模型常用字段

    一.ORM简介 ORM ,全称Object Relational Mapping,中文叫做对象关系映射,通过ORM我们可以通过类的方式去操作数据库,而不用再写原生的SQL语句.通过把表映射成类,把行作 ...

  2. Java并发编程73道面试题及答案 —— 面试稳了

    今天主要整理一下 Java 并发编程在面试中的常见问题,希望对需要的读者有用. 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任 ...

  3. Java 判断字符串是否为空的四种方法、优缺点与注意事项

    以下是Java 判断字符串是否为空的四种方法: 方法一: 最多人使用的一个方法, 直观, 方便, 但效率很低: if(s == null ||"".equals(s));方法二: ...

  4. thinkphp5 中使用 七牛云 上传图片和文件

    原文链接:http://www.thinkphp.cn/code/3279.html 参考:https://blog.csdn.net/rain_web/article/details/7910542 ...

  5. hog cython

    pip安装cython之后,将下面代码写入hogtest2.pyx文件(我通过改文件后缀新建) import numpy as np from PIL import Image cimport num ...

  6. DPAA1是如何辅助cpu进行网络加速的?

    1.为何会出现DPAA1? 1.1 如果没有多核处理器的出现可能就不会出现这个东东了! 1.2 怎么会跟多核处理器扯上关系呢? 1.2.1 先聊聊单核处理器会怎么处理网络包呢? 单核同一时刻只能处理一 ...

  7. UVALive 7503 Change(乱搞)题解

    题意:你现在有面额为A的纸币,现在需要面额为B的钱(可以是一张也可以是好多张拼成一张),有一台自动售货机,里面有任意价格的商品,售货机兑换出的零钱是随机的(比如找你0.03可能给你0.01+0.01+ ...

  8. 【Spring Security】四、自定义页面

    在前面例子中,登陆页面都是用的Spring Security自己提供的,这明显不符合实际开发场景,同时也没有退出和注销按钮,因此在每次测试的时候都要通过关闭浏览器来注销达到清除session的效果. ...

  9. C#Winform工具箱简介

    BindingSource:指定支持事务处理初始化Button:[按钮]用户单击它时引发事件 CheckBox:[复选框]允许用户选择或清除关联选项 CheckedListBox:[复选列表框]显示一 ...

  10. Gtk 窗口,控件,设置(添加图片等)

    1.关于窗口   // 创建顶层窗体,后面有POPUP的 GtkWidget *main_window; main_window = gtk_window_new (GTK_WINDOW_TOPLEV ...