单调队列及其DP优化_单调队列优化dp-程序员宅基地

技术标签: 算法  c++  大一算法学习  动态规划  

单调队列

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

滑动窗口

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

#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优化

最大字序和

在这里插入图片描述
求一下前缀和,对于一个点 i i i,我们要做的是求出 m a x ( s i − s j ) ( 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]为前 i i i头牛的最大方案数, f [ i ] = m a x ( f [ i − j − 1 ] + s u m [ i ] − s u m [ i − j ] ) ( j > = 1 , j < = m i n ( 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 ] − s u m [ i − j ] f[i-j-1]-sum[i-j] f[ij1]sum[ij]是一个可以维护的常量,满足和后面的量无关,可以使 g [ i ] = f [ i − 1 ] − s u m [ 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 ] > 0 c[1]+c[2]>0 c[1]+c[2]>0,则可以从1开到3,设 s u m [ i ] sum[i] sum[i] c [ i ] c[i] c[i]的前缀和,那么满足能从 i i i j j j的条件就是 s u m [ j − 1 ] − s u m [ i − 1 ] > = 0 sum[j-1]-sum[i-1]>=0 sum[j1]sum[i1]>=0,从 2 ∗ n 2*n 2n的位置开始往前看,处理区间内最小的 s u m [ j ] ( j > i 且 j < = i + n ) sum[j](j>i且j<=i+n) sum[j](j>ij<=i+n)的值,如果 s u m [ j ] − s u m [ i ] > = 0 sum[j]-sum[i]>=0 sum[j]sum[i]>=0则代表 i + 1 i+1 i+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;
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/stldecember/article/details/127972131

智能推荐

搭建Vulhub靶场 【附图】_vulhub靶场搭建-程序员宅基地

文章浏览阅读2.1w次,点赞59次,收藏265次。目录0x01简单概述0x02安装环境1. kali设置2. 更新软件源中的所有软件列表3. 安装https协议及CA证书0x03安装步骤一、安装Docker1. 下载安装2. 查看Docker是否安装成功3. 查看docker基本信息二、安装vluhub1. 安装pip32. 安装Docker-Compose3. 查看docker-compose版本三、安装vulhub靶场1. 克隆下载2. 随便进入一个靶场环境目录3. 对靶场进行编译4. 运行此靶场5. 查看启动环境6. 通过浏览器访问7. 关闭此靶场环_vulhub靶场搭建

sensei鼠标测试软件,「硬核测试:游戏鼠标精准度」赛睿SENSEI 310-程序员宅基地

文章浏览阅读564次。原标题:「硬核测试:游戏鼠标精准度」赛睿SENSEI 310作为赛睿最热销游戏鼠标之一,310有SENSEI(对称)和RIVAL(右手)两个版本,均采用今天要测的TrueMove3引擎,是基于PMW3360打造的1:1真实追踪的引擎,虽然现在“1:1引擎”很多了,但TrueMove出来时这个概念还是很新颖的,尤其是提到了消除抖动,最大限度的保持理论和实际DPI的稳定性,那么到底是不是真的1:1呢,..._鼠标精确度检测软件

国际酒店预订APP_基于android平台的酒店预订管理系统软件设计的论文-程序员宅基地

文章浏览阅读209次,点赞6次,收藏5次。随着人们生活水平的提高和旅游业的迅速发展,国际酒店的预订需求越来越大。为满足用户的需求,安卓国际酒店预订APP应运而生。本文旨在详细介绍该APP的设计与实现过程,以提供方便、快捷、安全的酒店预订服务。首先,本文将介绍课题的背景和国内外现状与趋势。随着国内外旅游业的快速发展,人们对旅游住宿的需求也越来越高。同时,随着移动互联网的普及,手机APP已经成为人们预订酒店的首选方式。因此,开发一款便捷、实用的酒店预订APP已经成为当务之急。接着,本文将详细阐述系统的设计和实现过程。_基于android平台的酒店预订管理系统软件设计的论文

DirectX9 ShadowMap例子学习笔记_g_aminitobjworld-程序员宅基地

文章浏览阅读2.7k次。本文版权归博客园 mavaL所有,如有转载请按如下方式详细标明原创作者及出处,以示尊重!!原创作者:mavaL原文链接:DirectX9 ShadowMap例子学习笔记学习SDK例子真是快速加强编程能力的途径,然而虽如此,微软不仅在每个例子中展示了本_g_aminitobjworld

Bootstrap datetimepicker- Uncaught TypeError : Cannot to read property “getTime” of undefined_bootstrap-datetimepicker.min.js?v=2.4.4:1 uncaught-程序员宅基地

文章浏览阅读3.6k次。解决:在bootstrap-datetimepicker.js中,找到getDate: function () { var d = this.getUTCDate(); if (d === null) { return null; } return new Date(d.getTime() + (d.getTimezoneOf..._bootstrap-datetimepicker.min.js?v=2.4.4:1 uncaught typeerror: cannot read pr

C语言实现大数相乘运算_两个大数相乘c语音-程序员宅基地

文章浏览阅读1.3k次。本篇文章依然是有关TP2的内容。TP2主要思想:跳出整型浮点型的限制,定义新的容量比较大的数据类型,从而实现一些大数运算看了一些网上的算法和代码,也从前辈文章里得到一些灵感,产出一个用C语言实现大数相乘的算法废话不多说,直接上算法和代码t_EntierLong multiplication(t_EntierLong n1,t_EntierLong n2){ int i,j,m,c;//m是进位变量 t_EntierLong result; result.Negatif=f_两个大数相乘c语音

随便推点

mybatis根据数组批量查询_前端传入的是long型的数组后端在mybatis中怎么批量查询-程序员宅基地

文章浏览阅读1.8k次。接口/** * 从页面接收的数据是多值数据,就是一个数组,它不想转成其它类型,直接把数组丢给dao */ public List<Emp> queryByArray(Integer[] empnos);EmpMapper.xml配置文件<select id="queryByArray" resultType="emp"> select <incl..._前端传入的是long型的数组后端在mybatis中怎么批量查询

python词云是什么意思_python生成词云-程序员宅基地

文章浏览阅读715次。前言在大数据时代,你竟然会在网上看到的词云,例如这样的。看到之后你是什么感觉?想不想自己做一个?如果你的答案是正确的,那就不要拖延了,现在我们就开始,做一个词云分析图,Python是一个当下很流行的编程语言,你不仅可以用它做数据分析和可视化,还能用来做网站、爬取数据、做数学题、写脚本替你偷懒……如果你之前没有编程基础,没关系。希望你不要限于浏览,而是亲自动手尝试一番。到完成的那一步,你不仅可以做出..._词云是什么意思

nginx_nginxl-程序员宅基地

文章浏览阅读469次。什么是nginxNginx (engine x) 是一个高性能的HTTP和反向代理web服务器,使用c语言编写的一款web服务软件.Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。为什么使用nginx?作用1.反向代理2.负载均衡。3.._nginxl

英语 | Day 33、34 x 句句真研每日一句(找从句、意译)_句句真研每日一句答案在哪-程序员宅基地

文章浏览阅读465次,点赞2次,收藏3次。Day 33Day 34_句句真研每日一句答案在哪

python全栈指的是什么意思?这篇文章非常值得一看_什么是python全栈-程序员宅基地

文章浏览阅读620次。所以说一个现代化的项目,是一个非常复杂的构成,我们需要一个人来掌控全局,他不需要是各种技术的资深专家,但他需要熟悉到各种技术。全栈只是个概念,我们要明白全栈也是有分非常多类别的,真正的全栈工程师涵盖了web开发,DBA、爬虫、测试等各种技能,要学的内容也是相当大的。很多小伙伴想知道python全栈是什么意思,那么今天小编就通过这篇文章来给大家详细讲解一下什么是python的全栈,感兴趣的小伙伴一定要耐心阅读一下这篇文章。以上就是小编给大家带来的python全栈是什么意思的相关知识了。_什么是python全栈

Nginx-GridFS踩坑记录-程序员宅基地

文章浏览阅读709次。nginx和nginx-gridfs都装好了,有些坑可能和版本有很大关系Nginx重新编译安装后访问不了了把项目配置文件的导入注释掉,直接用最小配置测试server { listen 80; server_name www.wyyxhlxy.com; location / { root html; index i..._nginx invalid mongo user/pass: