算法笔记--单调队列优化dp
单调队列:队列中元素单调递增或递减,可以用双端队列实现(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的更多相关文章
- 「学习笔记」单调队列优化dp
目录 算法 例题 最大子段和 题意 思路 代码 修剪草坪 题意 思路 代码 瑰丽华尔兹 题意 思路 代码 股票交易 题意 思路 代码 算法 使用单调队列优化dp 废话 对与一些dp的转移方程,我们可以 ...
- 【笔记篇】单调队列优化dp学习笔记&&luogu2569_bzoj1855股票交♂易
DP颂 DP之神 圣洁美丽 算法光芒照大地 我们怀着 崇高敬意 跪倒在DP神殿里 你的复杂 能让蒟蒻 试图入门却放弃 在你光辉 照耀下面 AC真心不容易 dp大概是最经久不衰 亘古不化的算法了吧. 而 ...
- 2018.09.10 bzoj1499: [NOI2005]瑰丽华尔兹(单调队列优化dp)
传送门 单调队列优化dp好题. 这题其实很简单. 我们很容易想到一个O(T∗n∗m)" role="presentation" style="position: ...
- 单调队列优化DP,多重背包
单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...
- 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 ...
- hdu3401:单调队列优化dp
第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次 ...
- Parade(单调队列优化dp)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2490 Parade Time Limit: 4000/2000 MS (Java/Others) ...
- BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP
BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP Description 有一排n棵树,第i棵树的高度是Di. MHY要从第一棵树到第n棵树去找他的妹子玩. 如果MHY在 ...
- 【单调队列优化dp】 分组
[单调队列优化dp] 分组 >>>>题目 [题目] 给定一行n个非负整数,现在你可以选择其中若干个数,但不能有连续k个数被选择.你的任务是使得选出的数字的和最大 [输入格式] ...
随机推荐
- django创建ORM模型、通过ORM模型操作单个表、ORM模型常用字段
一.ORM简介 ORM ,全称Object Relational Mapping,中文叫做对象关系映射,通过ORM我们可以通过类的方式去操作数据库,而不用再写原生的SQL语句.通过把表映射成类,把行作 ...
- Java并发编程73道面试题及答案 —— 面试稳了
今天主要整理一下 Java 并发编程在面试中的常见问题,希望对需要的读者有用. 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任 ...
- Java 判断字符串是否为空的四种方法、优缺点与注意事项
以下是Java 判断字符串是否为空的四种方法: 方法一: 最多人使用的一个方法, 直观, 方便, 但效率很低: if(s == null ||"".equals(s));方法二: ...
- thinkphp5 中使用 七牛云 上传图片和文件
原文链接:http://www.thinkphp.cn/code/3279.html 参考:https://blog.csdn.net/rain_web/article/details/7910542 ...
- hog cython
pip安装cython之后,将下面代码写入hogtest2.pyx文件(我通过改文件后缀新建) import numpy as np from PIL import Image cimport num ...
- DPAA1是如何辅助cpu进行网络加速的?
1.为何会出现DPAA1? 1.1 如果没有多核处理器的出现可能就不会出现这个东东了! 1.2 怎么会跟多核处理器扯上关系呢? 1.2.1 先聊聊单核处理器会怎么处理网络包呢? 单核同一时刻只能处理一 ...
- UVALive 7503 Change(乱搞)题解
题意:你现在有面额为A的纸币,现在需要面额为B的钱(可以是一张也可以是好多张拼成一张),有一台自动售货机,里面有任意价格的商品,售货机兑换出的零钱是随机的(比如找你0.03可能给你0.01+0.01+ ...
- 【Spring Security】四、自定义页面
在前面例子中,登陆页面都是用的Spring Security自己提供的,这明显不符合实际开发场景,同时也没有退出和注销按钮,因此在每次测试的时候都要通过关闭浏览器来注销达到清除session的效果. ...
- C#Winform工具箱简介
BindingSource:指定支持事务处理初始化Button:[按钮]用户单击它时引发事件 CheckBox:[复选框]允许用户选择或清除关联选项 CheckedListBox:[复选列表框]显示一 ...
- Gtk 窗口,控件,设置(添加图片等)
1.关于窗口 // 创建顶层窗体,后面有POPUP的 GtkWidget *main_window; main_window = gtk_window_new (GTK_WINDOW_TOPLEV ...