单调队列优化DP模型整理

135. 最大子序和(活动 - AcWing)

找一个长度不超过m的连续子序列,但是并未指定这个子序列的长度,所以长度就有很多种选择,要获取任意一段长度的序列的区间和,那么显然要用到前缀和。然后我们来考虑,讨论以每个点作为结尾的序列显然可以将所有情况都不重不漏地考虑进去。那么就是考虑如何获得以某个节点作为结尾的子序列,长度为m,显然有一个思路就是暴力求解,即第一维循环尾节点,第二维循环往前延伸多少,实现是一定可以实现的,而时间复杂度就很高了,大概O(nm),所以我们要考虑考虑还有没有其他的解法。我们看一下求的过程,显然是用尾节点的前缀和减去前面的合法位置的前缀和,如果有一个点的前缀和小于它前面所有合法位置的前缀和,那么显然它前面所有位置的前缀和都不会再被用到,因为用这个点一定更优,所以我们相当于有一个容器来维护当前节点之前的合法位置的前缀和,然后保证最前面的元素一定是最小的,那么就成了滑动窗口问题。

#include<bits/stdc++.h>
using namespace std;
int a[300010],q[300010];
int main()
{int n,m;scanf("%d%d",&n,&m);int res;for(int i=1;i<=n;i++) {scanf("%d",&a[i]),a[i]+=a[i-1];if(1!=i) res=min(a[i],res);else res=a[i];}int hh=0,tt=0;//tt就指向末尾位置,将hh置空for(int i=1;i<=n;i++)//以i作为结尾,前面最多用到i-m+1位,但是求前缀和要再往前一位,故而就是i-m{while(q[hh]<i-m) hh++;//因为i-1肯定被放入了,所以不用担心队列为空,即使最开始0<1也是可以的res=max(res,a[i]-a[q[hh]]);//hh>tt的时候相当于弹空,弹空位置是0,刚好while(hh<=tt&&a[q[tt]]>=a[i]) tt--;q[++tt]=i;//相当于已经放入了0,从1开始放前缀和}cout<<res;
}

 ps:单调队列自己写清楚就行,不是非得这么模拟。

1088. 旅行问题(活动 - AcWing)

如图,我们对于每个起点都需要判断,同时还需要判断是顺时针走还是逆时针走,只要两者之间有一个满足能够回到起点,那么就是该点就是可以的。 

顺时针的如下考虑:

所以最核心的地方在于在数轴上正确找到我们要求的区间。

哦另外,这个题的数据范围可能会爆int,记得处理一下

#include<bits/stdc++.h>
#define int long long
using namespace std;
int o[1000010],d[1000010],s[2000010],ans[1000010],q[2000010],hh,tt;
signed main()
{int n;scanf("%lld",&n);for(int i=1;i<=n;i++) scanf("%lld%lld",&o[i],&d[i]);//顺时针for(int i=1;i<=n;i++) s[i+n]=s[i]=o[i]-d[i];for(int i=1;i<=2*n;i++) s[i]+=s[i-1];hh=tt=0;for(int i=2*n;i>=0;i--){while(hh<tt&&q[hh]>i+n)hh++;if(i<n&&s[q[hh]]-s[i]>=0) {// printf("1:%d %d\n",i,q[hh]);ans[i+1]=1;}while(hh<tt&&s[q[tt-1]]>=s[i]) tt--;q[tt++]=i;}//逆时针d[0]=d[n];for(int i=1;i<=n;i++) s[i+n]=s[i]=o[i]-d[i-1];for(int i=1;i<=2*n;i++) s[i]+=s[i-1];hh=0,tt=0;for(int i=1;i<=2*n;i++){//4-1while(hh<tt&&q[hh]<i-n)hh++;if(i>n){if(s[i]-s[q[hh]]>=0) {ans[i-n]=1;// printf("2:%d %d %d\n",i,q[hh],i-n);}}while(hh<tt&&s[q[tt-1]]<=s[i]) tt--;q[tt++]=i;}for(int i=1;i<=n;i++) {if(ans[i]==1) printf("TAK\n");else printf("NIE\n");}
}

1087. 修剪草坪(活动 - AcWing)

这里首先是要得到最大效益,然后是不能有连续超过k只奶牛被选中。,然后要求最大效益。

我们可以定义dp[i]表示从前i头牛中选的合法方案。

那么第i头牛可以选也可以不选,如果不选的话,那么直接从i-1头牛出转移状态即可

如果选,那么就要注意了,我们要保证不超过连续k头被选

那么这里就需要讨论一下以i为结尾,连续多少头牛被选:

假设连续j头被选:1<=j<=k:

那么我们第i-j+1头牛是被选区间的左边界,i-j头牛一定不能被选,否则就不是连续j头牛了,那么i-j头牛在哪个值中一定不被选呢,很显然是dp[i-j-1],因为这是从前i-j-1头牛中选的合法情况,肯定不包含第i-j头牛,那么转移方程就出来了。另外我们要快速获得连续j头牛的区间和,用前缀和处理一下就好。

状态计算部分代码如下: 

dp[1]=w[1];for(int i=2;i<=n;i++){dp[i]=dp[i-1];for(int j=1;j<=k&&i-j>=0;j++){int l=max(i-j-1,0);dp[i]=max(dp[i],dp[l]+w[i]-w[i-j]);}}

显然这个嵌套循环的时间复杂度有点高,很容易超时,那么我们观察一下,往前延伸j个,要求最大值,那么不就是在前面长度为j的窗口中求最大值嘛,用单调队列优化一下即可。我们再来观察下递推的式子:dp[i]=dp[i-j-1]+w[i]-w[i-j],显然与j有关的有两个值,所以我们需要按照这两个值来维护滑动窗口。我们可以定义一个函数来获取dp[i-j-1]-w[i-j]的值,进而维护单调队列。

另外初始的时候需要把0放入队列,因为前k个更新的时候可以延伸到开头,那么计算就要用到0处的值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll w[100010],dp[100010];
int q[100010],hh,tt;
ll get(int i)
{if(!i) return 0;return dp[i-1]-w[i];
}
int main()
{int n,k;scanf("%d%d",&n,&k);for(int i=1;i<=n;i++){scanf("%lld",&w[i]);w[i] += w[i-1];}hh=0,tt=1;//初始将0放入for(int i=1;i<=n;i++){dp[i]=dp[i-1];while(hh<tt&&q[hh]<i-k) hh++;dp[i]=max(dp[i],w[i]+get(q[hh]));while(hh<tt&&get(q[tt-1])<=get(i)) tt--;q[tt++]=i;}cout<<dp[n];
}

 1089. 烽火传递(活动 - AcWing)

思路:这题是连续m个至少有一个发出信号,然后要求花费最小值。这里如果第i个发出信号,那么前面只要第i-m个发出信号即可,中间的发不发都无所谓,不发最好。如果第i个不发出信号,那么前面[i-m+1,i-1]中必须有一个发出信号。而且我们要确保它发信号,那么就不能笼统的定义从前i个中选,必须要精确到它发不发。可以多加一维表示该点是否发信号,当然也可以定义dp[i]表示第i个点发出信号,那么我们可以来枚举上一个发信号的位置j,显然j应该满足i-m<=j<=i-1,如果j再往前,那么显然就不满足条件了。那么我们的转移方程就出来了。不过很显然,如果要往前枚举的话时间应该会超,而且我们想要的只是前面一段区间中的最小值,所以用单调队列维护即可。

另外为了方便计算,自然要把0放入队列。还有就是答案也不一定就是最后一个位置放,我们需要从最后m个位置中找,有一个位置放即可。

#include<bits/stdc++.h>
using namespace std;
int q[200010],dp[200010],a[200010],hh,tt;
int main()
{int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}hh=0,tt=1;for(int i=1;i<=n;i++){while(hh<tt&&q[hh]<i-m)hh++;dp[i]=dp[q[hh]]+a[i];//printf("%d %d %d\n",i,q[hh],dp[i]);while(hh<tt&&dp[q[tt-1]]>=dp[i]) tt--;q[tt++]=i;}int mi=0x3f3f3f3f;for(int i=n-m+1;i<=n;i++){mi=min(mi,dp[i]);}cout<<mi;
}

1090. 绿色通道(1090. 绿色通道 - AcWing题库)

思路:这道题需要求的是在总花费有限制的情况下,至少可以连续多少个不选。

这里问的是至少连续多少个,那么显然答案有很多,而且合法答案是满足单调性的,我们抽象的来看一下,很显然如果空的题多了,那么花费肯定不会超过t,如果空的题少了那么花费肯定又大于t,这里可能会疑惑是否严格满足单调性,但是我们只找最小的方案,那么就是小于这个方案的花费肯定大于t,大于这个方案的花费小于t,那么就可以再往下一点去找。所以我们只需要二分空的长度,然后在check函数里面用动态规划计算最小花费,判断是否小于t即可。

然后现在最关键的就是check函数怎么写,这里显然是要求最多空x长度时,花费最小是多少,那么和烽火台就一样了,我们定义dp[i]表示第i个位置点火,那么去找上一个点火的位置即可。

实际上还是有区别的,上题是m个中至少有一个发送信号,这里实际上可以连续m个空着,那么应该是连续m+1个至少有一个发射信号。

#include<bits/stdc++.h>
using namespace std;
int n,t;
int a[50010],dp[50010],q[50010],hh,tt;
int check(int x)
{memset(dp,0,sizeof dp);hh=0,tt=1;q[hh]=0;for(int i=1;i<=n;i++){while(hh<tt&&q[hh]<i-x-1) hh++;dp[i]=dp[q[hh]]+a[i];while(hh<tt&&dp[q[tt-1]]>=dp[i]) tt--;q[tt++]=i;}int mi=0x3f3f3f3f;for(int i=n-x;i<=n;i++) mi=min(mi,dp[i]);// printf("%d\n",mi);if(mi<=t) return 1;else return 0;
}
int main()
{scanf("%d%d",&n,&t);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}int l=0,r=n;while(l<r){int mid=(l+r)/2;if(check(mid)) r=mid;//空的越少越好else l=mid+1;}cout<<l;
}

1091. 理想的正方形(活动 - AcWing)

 如图,对于一个n*n的矩形,我们将每行的最值累计到最右边,再将这一列的最值累计到右下角,那么预处理后就很容易获得这个区间的最值了。

如图的紫色区域就存了每个n*n矩形的最值。这个思路很简单,我们需要注意的就是下标的处理。

这里还有一个知识点,对于一个二维数组w[][],w[i]实际上可以用作一个一维数组。那么处理行就很简单了。对于每一行统计定长窗口中的最值。

统计列的时候,我们先一列一列的将每一列装进一个空的一维数组,然后再对这个一维数组进行上述区间找最值得操作。因为并不涉及区间与区间之间,所以我们只要对于每一个区间处理明白就行。

#include<bits/stdc++.h>
using namespace std;
int w[1010][1010],rmx[1010][1010],rmi[1010][1010],t1[1010],t2[1010],c[1010],d[1010],hh,tt,q[1010];
int n;
void get_max(int a[],int b[],int s)
{hh=tt=0;//可以为空for(int i=1;i<=s;i++){while(hh<tt&&q[hh]<i-n+1) hh++;while(hh<tt&&a[q[tt-1]]<=a[i]) tt--;q[tt++]=i;b[i]=a[q[hh]];}
}
void get_min(int a[],int b[],int s)
{hh=tt=0;for(int i=1;i<=s;i++){while(hh<tt&&q[hh]<i-n+1) hh++;while(hh<tt&&a[q[tt-1]]>=a[i]) tt--;q[tt++]=i;b[i]=a[q[hh]];}
}
int main()
{int a,b;scanf("%d%d%d",&a,&b,&n);for(int i=1;i<=a;i++)for(int j=1;j<=b;j++) scanf("%d",&w[i][j]);for(int i=1;i<=a;i++) get_max(w[i],rmx[i],b),get_min(w[i],rmi[i],b);int res=0x3f3f3f3f;for(int j=n;j<=b;j++)//列{for(int i=1;i<=a;i++) t1[i]=rmx[i][j];get_max(t1,c,a);for(int i=1;i<=a;i++) t2[i]=rmi[i][j];get_min(t2,d,a);for(int i=n;i<=a;i++)  res = min(res,c[i]-d[i]);}cout<<res;
}

ps:数组的值传参可以修改数组。

虽然但是,实际上只有两种类型直接用上了dp,一种是要选尽可能多,但不能有超过连续k个被同时选,一种是选尽可能少,不能有连续超过k个不被选,dp时都是以i被选作为状态表示,去找上一个被选位置进而解决的。剩下的侧重单调队列,而非dp。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/654833.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Spring Boot的饮食分享平台设计与实现

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

【Javaweb程序设计】【C00161】基于SSM电子产品交易管理系统(论文+PPT)

基于SSM电子产品交易管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的电子产品交易系统 本系统分为前台用户和后台管理员2个功能模块. 前台用户模块&#xff1a;当游客打开系统的网址后&#xff0c;首先看到的就…

738. 单调递增的数字 - 力扣(LeetCode)

题目描述 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 题目示例 输入: n 332 输出: 299 解题思路 题目要求小于等于N的最…

换个思路快速上手UML和plantUML——时序图

上一章我们介绍了类图&#xff0c;我们很清楚&#xff0c;类图是从更加宏观的角度去梳理系统结构的&#xff0c;从类图中我们可以获取到类与类之间&#xff1a;继承&#xff0c;实现等关系信息&#xff0c;是宏观逻辑。下面我们继续换一个思路&#xff1a;作为一名软件工程结构…

给刚上小学的侄女准备新年礼物,有什么让小朋友喜欢的玩具推荐?

给刚上小学的侄女准备新年礼物&#xff0c;我觉得也是有很多选择的。因为现在的市场上款式太多了&#xff0c;选择自己心意的适合小侄女的都是可以的。但是如果非要选益智的或是智能高科技的&#xff0c;对孩子来说既能玩耍又能在玩的同时学习到知识&#xff0c;能够开拓孩子眼…

无限可能!安全狗入选“潜力十强企业”

近日&#xff0c;等级保护测评公布了“2023年网络安全优秀评选”活动评选结果。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借突出的综合实力&#xff0c;荣获“潜力十强企业”称号。 厦门服云信息科技有限公司&#xff08;品牌名&#xff1a;安全狗&#xff09;创办于20…

uniapp微信小程序-前端设计模式学习(中)

三、工厂模式 通俗解释&#xff08;理解记忆&#xff09; 假设我们有一个汽车工厂。我们可以让工厂根据用户的选择生产不同型号的汽车&#xff0c;而用户无需知道具体的汽车制造过程。 工厂模式的优势在于&#xff0c;它隐藏了对象的创建细节&#xff0c;让客户端代码更简洁…

《HTML 简易速速上手小册》第10章:HTML 的维护与优化(2024 最新版)

文章目录 10.1 网页性能优化10.1.1 基础知识10.1.2 案例 1&#xff1a;优化网页图像10.1.3 案例 2&#xff1a;使用延迟加载优化性能10.1.4 案例 3&#xff1a;优化 CSS 和 JavaScript 的加载 10.2 SEO 最佳实践10.2.1 基础知识10.2.2 案例 1&#xff1a;创建一个 SEO 友好的博…

数据库管理-第139期 做大还是做小-Oracle名称哪些事(20240125)

数据库管理139期 2024-01-25 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09;1 问题2 排查3 扩展总结 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle A…

麒麟系统—— openKylin 安装 mongodb

麒麟系统—— openKylin 安装 mongodb 一、准备工作1. 确保麒麟系统 openKylin 已经安装完毕。 二、下载解压 MongoDB二、增加环境变量三、配置MongoDB创建数据目录创建日志文件运行 四、加入到服务中 MongoDB是一款高性能、开源的NoSQL数据库&#xff0c;因其灵活的数据结构、…

python第五节:集合set(3)

集合遍历 for循环遍历集合中元素 例子1&#xff1a; set1 {a,b,cde,张三,123} for i in set1: print(i) 结果&#xff1a; a cde b 张三 123 enumerate遍历索引和元素 例子2&#xff1a; set1 {a,b,cde,张三,123} for index,value in enumerate(set1): print(index…

MyBatis 的注解实现方法

MyBatis 的注解实现方法 MyBatis 的注解实现方法引入依赖添加配置创建表创建实体类创建mapper接口InsertDeleteSelectResults和ResultMap通过配置文件解决 UpdateOptions MyBatis 的注解实现方法 引入依赖 在springBoot项目中下载了EditStarters插件的,可以直接在配置文件处右…

效率高的B树系列

文章目录 前言B树概念性质插入数据分析代码实现性能分析 B树概念特性插入数据分析应用 B*树概念B*树的分裂 总结B树系列的区别B树系列对比哈希和平衡搜索树 前言 前面我们所学习到的数据结构&#xff0c;只能用来存储少量的数据&#xff0c;因为内存大小是非常有限的&#xff…

obs-studio 源码学习 obs.h

obs.h 引用头文件介绍 c99defs.h&#xff1a;这个头文件提供了一些 C99 标准的定义和声明&#xff0c;包括一些常用的宏定义和类型定义&#xff0c;用于提高代码的可移植性和兼容性。 bmem.h&#xff1a;这个头文件提供了对内存分配和管理的功能&#xff0c;包括一些内存分配…

一个查询IP地理信息和CDN提供商的离线终端工具Nali官方使用指南

Nali 一个查询IP地理信息和CDN提供商的离线终端工具. 功能 支持多种数据库 纯真 IPv4 离线数据库ZX IPv6 离线数据库Geoip2 城市数据库 (可选)IPIP 数据库 (可选)ip2region 数据库 (可选)DB-IP 数据库 (可选)IP2Location DB3 LITE 数据库 (可选)CDN 服务提供商查询支持管道处…

.ui文件相关

目录 ui类生成过程&#xff1a; 提问&#xff1a; 等以后自己熟练了用代码写这些样式内容&#xff0c;尽量用代码写&#xff0c;原因很简单&#xff1a; 用代码写的可以直接修改代码&#xff0c;但是在设计界面修改的东西&#xff0c;电脑没有QC这玩意&#xff0c;还真不好改…

Gitlab7.14 中文版安装教程

Gitlab7.14 中文版安装教程 注&#xff1a; 本教程由羞涩梦整理同步发布&#xff0c;本人技术分享站点&#xff1a;blog.hukanfa.com转发本文请备注原文链接&#xff0c;本文内容整理日期&#xff1a;2024-01-28csdn 博客名称&#xff1a;五维空间-影子&#xff0c;欢迎关注 …

JWT(JSON Web Token)详解以及在go-zero中配置的方法

目的 对用户进行身份认证和信息交换 RFC 7519 传统方式 通过session保存对话信息&#xff0c;服务端返回一个session id&#xff0c;用户保存这个id在cookie内&#xff0c;然后每次请求都传给服务端 局限性 对于服务器集群难以向每个服务器共享同一session jwt的方式是…

【vue】vue.config.js里面获取本机ip:

文章目录 一、效果&#xff1a;二、实现&#xff1a; 一、效果&#xff1a; 二、实现&#xff1a; const os require(os);function getLocalIpAddress() {const interfaces os.networkInterfaces();for (let key in interfaces) {const iface interfaces[key];for (let i …