单调队列及其DP优化

动态规划——单调队列优化DP 本题是一个二维单调队列问题,先对每一行求出以i位置结尾的长度不大于n的窗口的最大值与最小值,存放在数组row_max和row_min里面,再对于纵向求一遍,遍历row_max和row_min的每一列,便可以得到每一个正方形的最大值与最小值,做差即可。对于顺时针,维护一个前缀和数组s,s[i]表示从1位置出发到i+1位置时的剩余油量。集合:对于前i个烽火台,第i个烽火台被点燃的所有方案。集合:以i为右端点,长度不超过m的连续子序列的和。集合:对于前i道题,做了第i道题的所有方案。 阅读详情

单调队列

常应用于求一个固定滑动区间的最大值或者最小值。

滑动窗口

在这里插入图片描述
单调队列的经典题目,在一个区间内,如果要求最小值,如果后面的数既比前面的数位置靠后,又比前面的数小,就说明前面的数已经失去价值了,剔除前面的数(如果一个选手比你小又比你强,那么你该退役了),用一个单调队列来维护,每次的答案就是队首。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const int INF = 0x3f3f3f3f;
int a[N];
int q1[N];
int main(){
	int n,k;
	scanf("%d%d",&n,&k);
	for(int i = 1;i <= n;i ++)
		scanf("%d",&a[i]);
	int l = 1,r = 1;
	q1[1] = 0;
	a[0] = INF;
	for(int i = 1;i <= n;i ++){
		while(a[i] <= a[q1[r]] && r >= l) r --;//注意要先插入数再删除数,不然会出现奇奇怪怪的bug.
		q1[++ r] = i;
		while(q1[l] <= i - k) l ++;
		if(i >= k) cout << a[q1[l]] << " ";
	}
	l = 1,r = 1;
	q1[1] = 0;
	a[0] = -INF;
	cout << endl;
	for(int i = 1;i <= n;i ++){
		while(a[i] >= a[q1[r]] && r >= l) r --;
		q1[++ r] = i;
		while(q1[l] <= i - k) l ++;
		if(i >= k) cout << a[q1[l]] << " ";
	}
	return 0;
} 

单调队列DP优化

最大字序和

在这里插入图片描述
求一下前缀和,对于一个点iii,我们要做的是求出max(si−sj)(j>=i−m且j<=i−1)max(s_i - s_j)(j>=i-m且j<=i-1)max(sisj)(j>=imj<=i1),所以我们只需要维护一个长度为m的单调队列就可以了。

#include <bits/stdc++.h>
using namespace std;
const int N = 300010;
const int INF = 0x3f3f3f3f;
int a[N],q[N],s[N];
int main(){
	int n,m;
	cin >> n >> m;
	for(int i = 1;i <= n;i ++)
		scanf("%d",&a[i]);
	int h = 0,t = 0;
	int ans = -INF;
	for(int i = 1;i <= n;i ++)
		s[i] = s[i - 1] + a[i];
	for(int i = 0;i <= n;i ++){//注意计算答案的时候是否要保存这个数,这四句话的顺序应该有差别
		while(q[h] < i - m && h < t) h ++;
		if(i > 0) ans = max(s[i] - s[q[h]],ans);
		while(t >= h && s[i] <= s[q[t]]) t --;
		q[++ t] = i;
	}
	printf("%d",ans);
	return 0;
}

修剪草坪

在这里插入图片描述
考虑f[i]f[i]f[i]为前iii头牛的最大方案数,f[i]=max(f[i−j−1]+sum[i]−sum[i−j])(j>=1,j<=min(k,i).)f[i]=max(f[i-j-1]+sum[i]-sum[i-j])(j>=1,j<=min(k,i).)f[i]=max(f[ij1]+sum[i]sum[ij])(j>=1,j<=min(k,i).)发现是一个动态过程,如果想用单调队列维护,我们发现f[i−j−1]−sum[i−j]f[i-j-1]-sum[i-j]f[ij1]sum[ij]是一个可以维护的常量,满足和后面的量无关,可以使g[i]=f[i−1]−sum[i]g[i]=f[i-1]-sum[i]g[i]=f[i1]sum[i],维护g[i]g[i]g[i]的单调队列即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
typedef long long LL;
LL f[N],s[N],q[N],g[N];
int main(){
	int n,k,ans;
	cin >> n >> k;
	for(int i = 1;i <= n;i ++){
		scanf("%d",&s[i]);
		s[i] += s[i - 1];
	}
	int h = 0,t = 0;
	for(int i = 1;i <= n;i ++){
		g[i] = f[i - 1] - s[i];
		while(g[i] > g[q[t]] && t >= h) t --;
		q[++ t] = i;
		if(q[h] < i - k) h ++;
		f[i] = g[q[h]] + s[i];
	}
	cout << f[n];
	return 0;
}

旅行问题

在这里插入图片描述
特别逆天的一道题目,希望未来还能有耐心再来做一遍,做起来感觉不难但是边界处理很恶心人。先讲顺序的思路,先破环成链,搞成一条线,设o为油的数量,d为i到i+1的距离,c[i]=o[i]−d[i]c[i] = o[i] - d[i]c[i]=o[i]d[i]那么如果c[1]+c[2]>0c[1]+c[2]>0c[1]+c[2]>0,则可以从1开到3,设sum[i]sum[i]sum[i]c[i]c[i]c[i]的前缀和,那么满足能从iiijjj的条件就是sum[j−1]−sum[i−1]>=0sum[j-1]-sum[i-1]>=0sum[j1]sum[i1]>=0,从2∗n2*n2n的位置开始往前看,处理区间内最小的sum[j](j>i且j<=i+n)sum[j](j>i且j<=i+n)sum[j](j>ij<=i+n)的值,如果sum[j]−sum[i]>=0sum[j]-sum[i]>=0sum[j]sum[i]>=0则代表i+1i+1i+1的位置是合法的,逆时针就是反过来做一遍。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6;
LL s[N + 5];
int o[N + 5],dist[N + 5],q[N + 5];
bool ans[N + 5];
int main(){
	int n;
	cin >> n;
	for(int i = 1;i <= n;i ++)
		scanf("%d%d",&o[i],&dist[i]);
	for(int i = 1;i <= n;i ++)
		s[i] = s[i + n] = o[i] - dist[i];
	for(int i = 1;i <= 2 * n;i ++)
		s[i] += s[i - 1];
	int hh = 0,tt = -1;
	for(int i = 2 * n;i >= 0;i --){
		if(q[hh] > i + n) hh ++;
		if(s[q[hh]] - s[i] >= 0 && i < n) ans[i + 1] = true;
		while(tt >= hh && s[i] <= s[q[tt]]) tt --;
		q[++ tt] = i;
	}
	dist[0] = dist[n];
	for(int i = 1;i <= n;i ++)
		s[i] = s[i + n] = o[i] - dist[i - 1];
	for(int i = 2 * n;i >= 1;i --)
		s[i] += s[i + 1];
	hh = 0,tt = -1;
	for(int i = 1;i <= 2 * n;i ++){
		if(q[hh] < i - n) hh ++;
		while(tt >= hh && s[i] <= s[q[tt]]) tt --;
		q[++ tt] = i;
		if(i > n && s[i + 1] <= s[q[hh]]) ans[i - n] = true;
	}
	for(int i = 1;i <= n;i ++){
		if(ans[i]) printf("TAK\n");
		else printf("NIE\n");
	}
	return 0;
}

烽火传递

在这里插入图片描述
基于DP的一道题目,思维被之前的题目给局限住了,感觉一定要对某个值进行处理,但是我们可以对DP值进行处理,比如这道题目,我们设f[i]f[i]f[i]为i点放狼烟的最大值,那么只要维护f[i−m+1]f[i-m+1]f[im+1]f[i−1]f[i-1]f[i1]的最小值,f[i]f[i]f[i]就可以处理出来,就是之前的最小值加上当前的这个值,最后处理答案以及插入先后关系的时候要特判一下,思考要更加周全。

#include <bits/stdc++.h>
using namespace std;
const int N = 2 * 1e5;
int a[N + 5],f[N + 5],q[N + 5];
int main(){
	int n,m,minn = 0x3f3f3f3f;
	cin >> n >> m;
	for(int i = 1;i <= n;i ++) cin >> a[i];
	int hh = 0,tt = 0;
	for(int i = 1;i <= n;i ++){
		if(q[hh] < i - m && hh < tt) hh ++;
		f[i] = f[q[hh]] + a[i];
		while(f[i] < f[q[tt]] && tt >= hh) tt --;
		q[++ tt] = i;
	}
	for(int i = n - m + 1;i <= n;i ++)
		minn = min(f[i],minn);
	cout << minn;
	return 0;
}

绿色通道

在这里插入图片描述
和上一道问题差不多,来个二分选择一下不间断长度就可以了,不多解释。

#include <bits/stdc++.h>
using namespace std;
const int N = 5 * 1e4;
int a[N + 5],f[N + 5],q[N + 5];
int n,t;
bool check(int x){
	int hh = 0,ff = 0;
	q[0] = 0;
	for(int i = 1;i <= n;i ++){
		f[i] = f[q[hh]] + a[i];
		if(q[hh] < i - x && hh <= ff) hh ++;
		while(f[i] <= f[q[ff]] && ff >= hh) ff --;
		q[++ ff] = i;
	}
	int minn = 0x3f3f3f3f;
	for(int i = n - x;i <= n;i ++)
		if(f[i] <= minn) minn = f[i];
	if(minn <= t) return true;
	return false;
}
int main(){
	cin >> n >> t;
	for(int i = 1;i <= n;i ++)
		scanf("%d",&a[i]);
	int l,r,ans;
	l = 0,r = n;
	while(l <= r){
		int mid = l + r >> 1;
		if(check(mid)){
			ans = mid;
			r = mid - 1;
		}else l = mid + 1;
	}
	cout << ans;
	return 0;
}

理想的正方形

在这里插入图片描述
感觉单调队列优化DP的题目不算很难,这道题就是跑一个二维的单调队列优化就行,详见代码。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3;
int q[N + 5],q1[N + 5],tmp[N + 5][N + 5],tmp1[N + 5][N + 5][2];
int main(){
	int a,b,n;
	cin >> a >> b >> n;
	for(int i = 1;i <= a;i ++)
		for(int j = 1;j <= b;j ++)
			scanf("%d",&tmp[i][j]);
	for(int i = 1;i <= a;i ++){
		int hh = 0,tt = -1,hhh = 0,ttt = -1;
		q[hh] = 0,q1[hhh] = 0;
		for(int j = 1;j <= b;j ++){
			if(q[hh] <= j - n && hh <= tt) hh ++;
			while(tmp[i][j] <= tmp[i][q[tt]] && hh <= tt) tt --;
			q[++ tt] = j;
			tmp1[i][j][0] = tmp[i][q[hh]];
			if(q1[hhh] <= j - n && hhh <= ttt) hhh ++;
			while(tmp[i][j] >= tmp[i][q1[ttt]] && hhh <= ttt) ttt --;
			q1[++ ttt] = j;
			tmp1[i][j][1] = tmp[i][q1[hhh]];
		}
	}
	int minn = 0x3f3f3f3f;
	for(int j = n;j <= b;j ++){
		int hh = 0,tt = -1,hhh = 0,ttt = -1;
		for(int i = 1;i <= a;i ++){
			if(q[hh] <= i - n && hh <= tt) hh ++;
			while(tmp1[i][j][0] <= tmp1[q[tt]][j][0] && tt >= hh) tt --;
			q[++ tt] = i;
			if(q1[hhh] <= i - n && hhh <= ttt) hhh ++;
			while(tmp1[i][j][1] >= tmp1[q1[ttt]][j][1] && ttt >= hhh) ttt --;
			q1[++ ttt] = i;
			if(i >= n) minn = min(minn,tmp1[q1[hhh]][j][1] - tmp1[q[hh]][j][0]);
	//		cout << minn << " ";
		}
	//	cout << endl;
	}
	cout << minn;
	return 0;
}
OSMDroid(1)——OSM简介,基础例子搭建 1.OSMDroid简介 Osmdroid是一款完全开源的地图基本操作SDK,相比于现在普遍使用的百度地图,高德地图,谷歌地图等最大的好处就是开源。 最近在做一个军方项目,要求完全不能联网,从开发到应用整个阶段都是在局域网进行,相比之下采用了OSMDroid这个SDK作为解决方案。 先来看OSMDroid,便是(OpenStreetMapForAndroid),后面都简称OSM... 阅读详情

相关推荐

昇腾AI原生创新算子挑战赛(S1赛季)复盘-LessEqual算子

【代码】昇腾AI原生创新算子挑战赛(S1赛季)复盘-LessEqual算子。

william_myq的博客 487

线性DP例题(洛谷P5858 Golden Sword)及其单调队列优化

题目描述 制造一把金宝剑需要 n 种原料,编号为 1 到 n,编号为 i 的原料的坚固值ai。 炼金是很讲究放入原料的顺序的,因此小 E 必须按照 1 到 n 的顺序依次将这些原料放 入炼金锅。 但是,炼金锅的容量非常有限,它最多只能容纳 w 个原料。 所幸的是,每放入一个原料之前,小 E 可以从中取出一些原料,数量不能超过 s 个。 我们定义第 i 种原料的耐久度为:放入第 i 种原料时锅内的原料总数(包括正在放入的原料)× ai,则宝剑的耐久度为所有原料的耐久度之和。 小 E 当然想让他的宝剑的耐久度尽

Salvator_的博客 1581

OneNET微信小程序源码

OneNET微信小程序源码

单调队列优化DP问题

单调队列就是通过排除求最值时候的冗余,从而是队列具有性质,可以方便求解问题。

xjsc01的博客 1130

单调队列优化DP

单调队列优化DP最大子序和修剪草坪旅行问题烽火传递绿色通道理想的正方形 在多重背包问题3中,我们用到了单调队列优化DP,实际上,不止这个问题,很多问题我们仔细观察,就能发现它有类似的性质,我们就能用单调队列优化,我们通过几道题的练习来观察观察其中的规律。 最大子序和 题目链接:最大子序和 修剪草坪 题目链接:修剪草坪 旅行问题 题目链接:旅行问题 烽火传递 题目链接:烽火传递 绿色通道 题目链接:绿色通道 理想的正方形 题目链接:理想的正方形 ...

csdnwqy030429的博客 2696

单调队列优化 DP

的状态转移方程,标清楚 决策 的上下界,然后依据 转移 的性质用 单调队列维护 决策集合 ,进而优化复杂度。,而 决策集合 是从左向右滑动的,这样会不会导致某些后面的有效决策被排除?那么基于 减少无用决策出发:当本层的高度最大时,只保留 最小的 长度即可。位置的决策 排除了在它后面的部分元素,现在它出队了,谁有能是最优呢?整体上看,转移时需要维护到两个信息:当前层的长度,上一层的长度。时,首先让部分决策出队,并从 set 中删除,不需要更改。这是第一步的转移,去除了有空板 / 空人的影响。

qq_56326461的博客 761

单调队列优化dp

如上图,可以跳到 3−7 这个区间,跳到 3 和 4 的时候,都是消耗劳累度的,但是我们选择哪个更好呢,当然是越大的越好,因为高度越高,下一次越有利,如上图,当我们跳到 4 ,下一次跳到 3 的时候就不增加劳累度。如果暴力枚举那个区间,一般会超时,于是我们可以选择将最大值放入队头的最大值或者最小值,通过这样维护我们可以减去暴力枚举的过程,优化了时间复杂度。维护这样一个队列的好处是,当我们想求最大值的时候,我们只需要保证队列是单调递增的,于是每次取出队头就是我们的最大值。第一个元素下标进队列,此时队列为。

m0_74176921的博客 637

DP单调队列优化

单调队列->单调队列运用->单调队列优化->例题

WDamn6的博客 1281

DP 优化一:单调队列优化 DP

本文介绍了动态规划中的单调队列优化方法。首先解释了单调队列的概念及其在线性时间内求区间最值的优势,通过滑动窗口例题展示了具体实现过程。然后重点讲解了如何利用单调队列优化特定形式的DP转移方程,即形如f[i]=min{f[j]+F(j)+C}的情况。最后以一道粉刷围栏的题目为例,展示了如何将暴力DP解法转化为单调队列优化形式,通过数学变形将转移方程改写为可优化结构,从而显著提升算法效率。文章通过具体例题和代码示例,系统性地阐述了单调队列优化的原理和应用场景。

cwplh的博客 648

单调队列DP优化 --算法竞赛专题解析(13)

单调队列优化DP的原理,经典例题“多重背包”的多种解法

罗勇军的博客 5771

单调队列--优化dp

单调队列的定义:一种严格单调的队列(严格递增或者递减),其中队列中元素的位置也有一定的递增递减的性质。队首位置保存的是最优解,接着次优解。。 单调队列的维护(假设队列为严格递增队列): 1.新插入一个数到队尾(此时还没有插入),比较当前队尾的数和当前插入的数,如果当前插入的数小于队尾的数,弹出队尾的数。。。继续比较队尾的数和当前插入的数,直到队尾的数小于当前插入的数为止,在队尾插入当前

qwe2434127的专栏 488

震惊!单调队列优化DP竟如此简单——Monkey(原题:POI2014 Little Bird)

目录 前言 题目 初步分析  正解 结尾  前言 此题是用单调队列优化DP的实例,十分适合初学单调队列的人做,并且类似于单调队列优化多重背包,对今后进一步学习DP有很大的作用。 (如果你不知道什么是单调队列,看看这篇博客(矩形面积) LGOJ P3572 Little Bird   题目 问题 A(3768): 猴子 时间限制: 2 Sec  内存限制: 128 MB...

C20200521的博客 801

uestc599最小花费【单调队列优化dp

单调队列优化dp 单调队列是一种严格单调的队列,可以单调递增,也可以单调递减。队首位置保存的是最优解,第二个位置保存的是次优解,ect。。。 单调队列可以有两个操作: 1、插入一个新的元素,该元素从队尾开始向队首进行搜索,找到合适的位置插入之,如果该位置原本有元素,则替换它。 2、在过程中从队首删除不符合当前要求的元素。 单调队列实现起来可简单,可复杂。简

走一步再走一步 599

单调队列优化动态规划

什么类型的DP需要用到常规的单调队列?类似这样的转移方程可以用到单调队列: f[i]=max(g[j])+w[i]其中,g[j]是一个与i无关系的数。w[i]只与i有关系。怎么用?我们首先开一个队列。DP时: 1、先删掉前面超出范围的队头。 2、利用队头转移。 3、将这个数和队尾比较,若队尾不比它优,就删掉队尾,直到队列为空或队尾比它优。最后将它加进队尾。原因1、单调队列中的数都在要范围

剑锋OI SharpSwordOI 5924

HDU 5945 Fxx and game [单调队列+dp]【动态规划

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5945 ————————————.问题描述 青年理论计算机科学家Fxx给的学生设计了一款数字游戏。一开始你将会得到一个数:XX,每次游戏将给定两个参数:k,tk,t, 任意时刻你可以对你的数执行下面两个步骤之一:1.:X = X - i(1 <= i <= t)1.X=X−i(1<=i<=

Tabris的博客 447

POJ 1821 Fence 单调队列优化DP

题目大意:有K个工人,长度为N的墙(biaohao)

ACMmaxx的专栏 808

DP优化单调队列 专题

单调队列和之前之前的斜率优化有很大的交集。 DP优化之斜率优化 具体看wyn神牛的1D/1D动归优化。 Sliding window1 给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,如下表: Window position Min value Max value ...

alhe29828的博客 231

POJ 2373 单调队列优化DP

题意: 思路: f[i] = min(f[j]) + 1; 2 * a <= i - j <= 2 *b; i表示当前在第i个点。f[i]表示当前最少的线段个数 先是N^2的朴素DP(果断TLE) //By SiriusRen #include <cstdio> #include <cstring> #include &lt...

weixin_34233421的博客 157

poj 1821单调队列优化dp

#include&amp;lt;iostream&amp;gt; #include&amp;lt;cstdio&amp;gt; #include&amp;lt;algorithm&amp;gt; using namespace std; inline int read() { int r,s=0,c; for(;!isdigit(c=getchar());s=c); for(r=c^48;isdigit(c=getchar());(r*=

zhousc2003的博客 253
上一篇: 一元三次方程求解(二分法)
下一篇: 图的遍历+封锁阳光大学
一曲诉哀愁
博客等级 码龄7年 8粉丝 · 29原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值