博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
算法笔记--单调队列优化dp
阅读量:6405 次
发布时间:2019-06-23

本文共 6223 字,大约阅读时间需要 20 分钟。

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

代码:

#include
#include
#include
#include
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
#define pii pair
#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);//headconst int N = 1e6 + 5;int ans1[N], ans2[N];int main() { int n, k, x; scanf("%d %d", &n, &k); deque
mn, mx; for (int i = 1; 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+1) 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+1) 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 0;}
View Code

例2:

思路: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
#include
#include
#include
#include
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
#define pii pair
#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);//headconst int N = 1e5 + 5;LL sum[N], dp[N];int a[N];deque
q;multiset
s;int main() { int n; LL m; bool f = false; scanf("%d %lld", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); if(a[i] > m) f = true; sum[i] = sum[i-1] + a[i]; } if(f) return 0*puts("-1"); int p = 0; for (int i = 1; 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()-1] > 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() != 0)dp[i] = min(dp[i], *s.begin()); //cout << dp[i] << endl; } printf("%lld\n", dp[n]); return 0;}
View Code

例3: 

思路:

dp[0] = 0

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

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

#include
#include
#include
#include
#include
#include
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
#define pii pair
#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);//headconst int N = 1e6 + 5;int dp[N], cnt[N], pre[N], suf[N];deque
q;int main() { int n, L, a, b, l, r; while( ~scanf("%d %d", &n, &L)) { mem(cnt, 0); scanf("%d %d", &a, &b); a *= 2; b *= 2; for (int i = 0; i < n; i++) { scanf("%d %d", &l, &r); cnt[l+1]++; cnt[r]--; } for (int i = 1; i <= L; i++) cnt[i] += cnt[i-1]; q.clear(); q.push_back(0); for (int i = 2; i <= L; i += 2) { 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()] + 1; 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 0;}
View Code

 例4: 

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

代码:

#pragma GCC optimize(2)#pragma GCC optimize(3)#pragma GCC optimize(4)#include
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
#define pli pair
#define pii pair
#define piii pair
#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);//headconst int N = 7e3 + 5;int dp[N];int v[N], w[N], c[N];int main() { int n, m; scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d %d %d", &v[i], &w[i], &c[i]); for (int i = 1; i <= n; i++) { for (int p = 0; p < v[i]; p++) { int cnt = c[i]; deque
q; q.push_back({dp[p] + cnt*w[i], 0}); for (int tot = 1; 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 0;}
View Code

 

转载于:https://www.cnblogs.com/widsom/p/9298088.html

你可能感兴趣的文章
Oracle中NVARCHAR2与VARCHAR2的区别
查看>>
php debug
查看>>
新的一天,新的开始。
查看>>
<img> <a> CSS
查看>>
JS call和apply方法使用
查看>>
使用JavaScript设置、获取父子页面中的值
查看>>
在vue中如何实现购物车checkbox的三级联动
查看>>
华为C8812 手机连接电脑的问题
查看>>
Python3-装饰器
查看>>
读《More Effective C++中文版》笔记
查看>>
Elementui实战知识点随记
查看>>
Oracle10g RAC 修改IP [转载]
查看>>
关于char p[] = "hello world"; char *p = "hello world";
查看>>
FIREDAC的心得
查看>>
NPOI批量导入大量数据
查看>>
了解 Windows Azure 存储计费 – 带宽、事务和容量
查看>>
mysql5.7.22 zip 版安装
查看>>
【题解】最大公约数之和 V3 51nod 1237 杜教筛
查看>>
架构师速成6.7-设计开发思路-uml 分类: 架构师速成 ...
查看>>
js设置radio选中
查看>>