【单调队列单调栈专题】【蓝桥杯备考训练】:矩形牛棚、单调栈、滑动窗口、子矩阵、最大子序和、烽火传递【已更新完成】

目录

1、矩形牛棚(usaco training 6.1)

思路:

预处理的过程:

判断左右边界的过程:

代码:

2、单调栈(单调栈模板)

思路:

基本步骤:

1、维护单调性

2、处理要求的操作

3、入栈

代码:

3、滑动窗口(单调队列模板)

思路:

基本步骤(以求最大值为例):

1、维护单调性(在尾部做处理)

2、入队

3、判断是否滑出窗口,滑出则hh++

4、做要求的处理

代码:

4、子矩阵(第十四届蓝桥杯省赛C++ C组、第十四届蓝桥杯省赛Java C组/研究生组、第十四届蓝桥杯省赛Python A组)

思路:

固定左右边界进行枚举的过程:

实现得到滑动窗口最大值最小值的函数:

代码:

5、最大子序和(《算法竞赛进阶指南》)

思路:

滑动窗口中维护前缀和的单调性的步骤:

代码:

6、烽火传递(NOIP2010提高组初赛、《信息学奥赛一本通》)

思路:

维护单调性的步骤:

代码:


1、矩形牛棚(usaco training 6.1)

作为一个资本家,农夫约翰希望通过购买更多的奶牛来扩大他的牛奶业务。

因此,他需要找地方建立一个新的牛棚。

约翰购买了一大块土地,这个土地可以看作是一个 R行(编号 1∼R)C列(编号 1∼C)的方格矩阵。

不幸的是,他发现其中的部分方格区域已经被破坏了,因此他无法在整个 R×C 的土地上建立牛棚。

经调查,他发现共有 P 个方格内的土地遭到了破坏。

建立的牛棚必须是矩形的,并且内部不能包含被破坏的土地。

请你帮约翰计算,他能建造的最大的牛棚的面积是多少。

输入格式

第一行包含三个整数 R,C,P。

接下来 P 行,每行包含两个整数 r,c,表示第 r 行第 c 列的方格区域内土地是被破坏的。

输出格式

输出牛棚的最大可能面积。

数据范围

1≤R,C≤3000
0≤P≤30000
1≤r≤R1
1≤c≤C1

输入样例:
3 4 2
1 3
2 1
输出样例:
6
思路:

对于每行(预处理好每个方块上面最多能用的方块的个数),枚举其中的每列,分别判断左右两边第一列比该行少的位置,然后底乘高算出面积,维护最大值即可

预处理的过程:
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){if(g[i][j]==0){h[i][j]=h[i-1][j]+1;}}//记录这一位置上面能用的格子是多少 
判断左右边界的过程:
int work(int a[])
{a[0]=-1,a[m+1]=-1;int tt=0;//枚举左边第一个比这个位置上面能用的格子少的左边界 stk[++tt]=0;//把左边界加进去 for(int i=1;i<=m;i++){while(a[i]<=a[stk[tt]])tt--;//栈顶元素大于等于当前元素的话弹出栈顶 l[i]=stk[tt];stk[++tt]=i; } tt=0;stk[++tt]=m+1;for(int i=m;i>=1;i--){while(a[stk[tt]]>=a[i])tt--;r[i]=stk[tt];stk[++tt]=i;}int res=0;for(int i=1;i<=m;i++){res=max(res,a[i]*(r[i]-l[i]-1));}return res;
}
代码:
#include<bits/stdc++.h>using namespace std;const int N=3010;int n,m,p;int g[N][N],h[N][N];int l[N],r[N];int stk[N];int work(int a[])
{a[0]=-1,a[m+1]=-1;int tt=0;//枚举左边第一个比这个位置上面能用的格子少的左边界 stk[++tt]=0;//把左边界加进去 for(int i=1;i<=m;i++){while(a[i]<=a[stk[tt]])tt--;//栈顶元素大于等于当前元素的话弹出栈顶 l[i]=stk[tt];stk[++tt]=i; } tt=0;stk[++tt]=m+1;for(int i=m;i>=1;i--){while(a[stk[tt]]>=a[i])tt--;r[i]=stk[tt];stk[++tt]=i;}int res=0;for(int i=1;i<=m;i++){res=max(res,a[i]*(r[i]-l[i]-1));}return res;
}int main()
{cin>>n>>m>>p;for(int i=0;i<p;i++){int r,c;cin>>r>>c;g[r][c]=1;//标记为破坏 }for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){if(g[i][j]==0){h[i][j]=h[i-1][j]+1;}}//记录这一位置上面能用的格子是多少 int res=0;for(int i=1;i<=n;i++){res=max(res,work(h[i]));}cout<<res;return 0;
} 

2、单调栈(单调栈模板)

给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。

输入格式

第一行包含整数 N,表示数列长度。

第二行包含 N 个整数,表示整数数列。

输出格式

共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。

数据范围

1≤N≤1e5
1≤数列中元素≤1e9

输入样例:
5
3 4 2 7 5
输出样例:
-1 3 -1 2 2
思路:

经典单调栈模板

基本步骤:
1、维护单调性
while(tt>0 && s[tt]>=x)tt--;
2、处理要求的操作
if(tt<=0)cout<<"-1"<<" ";else cout<<s[tt]<<" ";
3、入栈
s[++tt]=x;
代码:
#include<bits/stdc++.h>using namespace std;const int N = 1e5 +5 ;int tt;
int s[N];int main()
{int n;cin>>n;while(n--){int x;cin>>x;while(tt>0 && s[tt]>=x)tt--;if(tt<=0)cout<<"-1"<<" ";else cout<<s[tt]<<" ";s[++tt]=x;}	return 0;
}

3、滑动窗口(单调队列模板)

给定一个大小为 n≤1e6 的数组。

有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。

你只能在窗口中看到 k 个数字。

每次滑动窗口向右移动一个位置。

以下是一个例子:

该数组为 [1 3 -1 -3 5 3 6 7],k 为 3。

窗口位置最小值最大值
[1 3 -1] -3 5 3 6 7-13
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 -3 [5 3 6] 736
1 3 -1 -3 5 [3 6 7]37

你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

输入格式

输入包含两行。

第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。

第二行有 n 个整数,代表数组的具体数值。

同行数据之间用空格隔开。

输出格式

输出包含两个。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
思路:

经典单调队列模板

基本步骤(以求最大值为例):
1、维护单调性(在尾部做处理)
while(hh<=tt && a[i]>dq[tt])tt--;
2、入队
dq[++tt]=a[i];
3、判断是否滑出窗口,滑出则hh++
if(i-k>=0 && dq[hh]==a[i-k])hh++;
4、做要求的处理
if(i+1>=k)cout<<dq[hh]<<" ";	
代码:
#include<bits/stdc++.h>using namespace std;const int N=1e6+5;int n,k;int tt=-1,hh;
int dq[N],a[N];int main()
{cin>>n>>k;for(int i=0;i<n;i++)scanf("%d",&a[i]);//求窗口最小值 for(int i=0;i<n;i++){while(hh<=tt && a[i]<dq[tt])tt--;dq[++tt]=a[i];//if(i-k>=0 && dq[hh]==a[i-k])hh++;//队头一定是最小的if(i+1>=k)cout<<dq[hh]<<" "; }	cout<<endl;//求最大值tt=-1,hh=0;for(int i=0;i<n;i++){while(hh<=tt && a[i]>dq[tt])tt--;dq[++tt]=a[i];if(i-k>=0 && dq[hh]==a[i-k])hh++;if(i+1>=k)cout<<dq[hh]<<" ";	} return 0;
}

4、子矩阵(第十四届蓝桥杯省赛C++ C组、第十四届蓝桥杯省赛Java C组/研究生组、第十四届蓝桥杯省赛Python A组)

给定一个 n×m(n 行 m 列)的矩阵。

设一个矩阵的价值为其所有数中的最大值和最小值的乘积。

求给定矩阵的所有大小为 a×b (a 行 b 列)的子矩阵的价值的和。

答案可能很大,你只需要输出答案对 998244353998244353 取模后的结果。

输入格式

输入的第一行包含四个整数分别表示 n,m,a,b相邻整数之间使用一个空格分隔。

接下来 n行每行包含 m 个整数,相邻整数之间使用一个空格分隔,表示矩阵中的每个数 A[i],[j]。

输出格式

输出一行包含一个整数表示答案。

数据范围

对于 40%40% 的评测用例,1≤n,m≤100
对于 70%70% 的评测用例,1≤n,m≤500;
对于所有评测用例,1≤a≤n≤1000,1≤b≤m≤1000,1≤Ai,j≤1e9。

输入样例:
2 3 1 2
1 2 3
4 5 6
输出样例:
58
样例解释

1×2+2×3+4×5+5×6=581×2+2×3+4×5+5×6=58。

思路:

固定左右边界进行枚举,这时候从上上边界往下边界枚举,每次在每个区域中在每行的最小值中选出最小的,在每行的最大值中选出最大的,这个区域的最大值乘最小值就是该矩阵的价值

固定左右边界进行枚举的过程:
	for(int i=b-1;i<m;i++)//固定好列区间 {for(int j=0;j<n;j++)A[j]=maxr[j][i];//每行:把每个窗口最大的数取出来getmax(A,B,n,a);//存到B中 for(int j=0;j<n;j++)A[j]=minr[j][i];//每行:把每个窗口最小的数取出来 getmin(A,C,n,a);//存到C中 for(int j=a-1;j<n;j++)	{res=(res+(LL)B[j]*C[j])%MOD;}}
实现得到滑动窗口最大值最小值的函数:
void getmin(int a[],int b[],int total,int lenth)
{int tt=-1,hh=0;for(int i=0;i<total;i++){//判断元素是否滑出窗口 if(hh<=tt && q[hh]<=i-lenth)hh++;//判断新元素和旧元素的大小关系确保队列单调while(hh<=tt && a[i]<=a[q[tt]])tt--; 	//入队 q[++tt]=i;//把最值交给存储数组 (队头一定是最小的) if(i>=lenth-1)b[i]=a[q[hh]];//i>=k-1表示滑动窗口形成}
}void getmax(int a[],int b[],int total,int lenth)
{int tt=-1,hh=0;for(int i=0;i<total;i++){if(hh<=tt && q[hh]<=i-lenth)hh++;while(hh<=tt && a[i]>=a[q[tt]])tt--;q[++tt]=i;//队头一定是最大的,赋值给b if(i>=lenth-1)b[i]=a[q[hh]];}
}
代码:
#include<bits/stdc++.h>using namespace std;const int N=1010;int n,m,a,b; int g[N][N],minr[N][N],maxr[N][N]; int q[N];typedef long long LL;const int MOD=998244353;void getmin(int a[],int b[],int total,int lenth)
{int tt=-1,hh=0;for(int i=0;i<total;i++){//判断元素是否滑出窗口 if(hh<=tt && q[hh]<=i-lenth)hh++;//判断新元素和旧元素的大小关系确保队列单调while(hh<=tt && a[i]<=a[q[tt]])tt--; 	//入队 q[++tt]=i;//把最值交给存储数组 (队头一定是最小的) if(i>=lenth-1)b[i]=a[q[hh]];//i>=k-1表示滑动窗口形成}
}void getmax(int a[],int b[],int total,int lenth)
{int tt=-1,hh=0;for(int i=0;i<total;i++){if(hh<=tt && q[hh]<=i-lenth)hh++;while(hh<=tt && a[i]>=a[q[tt]])tt--;q[++tt]=i;//队头一定是最大的,赋值给b if(i>=lenth-1)b[i]=a[q[hh]];}
}int main()
{cin>>n>>m>>a>>b;for(int i=0;i<n;i++)for(int j=0;j<m;j++)scanf("%d",&g[i][j]);//for(int i=0;i<n;i++)//for(int j=0;j<m;j++)printf("%d",g[i][j]);//预处理出来每一行中滑动窗口的最值 for(int i=0;i<n;i++){getmin(g[i],minr[i],m,b);getmax(g[i],maxr[i],m,b);}int res=0;int A[N],B[N],C[N];for(int i=b-1;i<m;i++)//固定好列区间 {for(int j=0;j<n;j++)A[j]=maxr[j][i];//每行:把每个窗口最大的数取出来getmax(A,B,n,a);//存到B中 for(int j=0;j<n;j++)A[j]=minr[j][i];//每行:把每个窗口最小的数取出来 getmin(A,C,n,a);//存到C中 for(int j=a-1;j<n;j++)	{res=(res+(LL)B[j]*C[j])%MOD;}}cout<<res;return 0;
}

5、最大子序和(《算法竞赛进阶指南》)

输入一个长度为 n 的整数序列,从中找出一段长度不超过 m 的连续子序列,使得子序列中所有数的和最大。

注意: 子序列的长度至少是 1。

输入格式

第一行输入两个整数 n,m。

第二行输入 n 个数,代表长度为 n 的整数序列。

同一行数之间用空格隔开。

输出格式

输出一个整数,代表该序列的最大子序和。

数据范围

1≤n,m≤300000
保证所有输入和最终结果都在 int 范围内。

输入样例:
6 4
1 -3 5 1 -2 3
输出样例:
7
思路:

前缀和+滑动窗口

滑动窗口中维护前缀和的单调性的步骤:
//s[q[tt]] 即将被减去,当然是越小越好 
//如果s[i](前i个数的和)小于s[q[tt]](前面的前缀和)
//那么s[i]更适合被减去来求后面的前缀和 
while(hh<=tt && s[q[tt]]>=s[i])tt--;
代码:
#include<bits/stdc++.h>using namespace std;const int N=300000+10;int n,m; long long  s[N];int q[N],hh=-1,tt;int main()
{cin>>n>>m;//前缀和 for(int i=1;i<=n;i++){cin>>s[i];s[i]+=s[i-1];}long long res=INT_MIN;for(int i=1;i<=n;i++){//判断是否滑出窗口 if(hh<=tt && q[hh]<=i-m-1)hh++;//保持单调性 //while(hh<=tt &&  )res=max(res,s[i]-s[q[hh]]);//s[q[tt]] 即将被减去,当然是越小越好 //如果s[i](前i个数的和)小于s[q[tt]](前面的前缀和)//那么s[i]更适合被减去来求后面的前缀和 while(hh<=tt && s[q[tt]]>=s[i])tt--;q[++tt]=i;}cout<<res;return 0;
} 

6、烽火传递(NOIP2010提高组初赛、《信息学奥赛一本通》)

烽火台是重要的军事防御设施,一般建在交通要道或险要处。

一旦有军情发生,则白天用浓烟,晚上有火光传递军情。

在某两个城市之间有 n 座烽火台,每个烽火台发出信号都有一定的代价。

为了使情报准确传递,在连续 m 个烽火台中至少要有一个发出信号。

现在输入 n,m 和每个烽火台的代价,请计算在两城市之间准确传递情报所需花费的总代价最少为多少。

输入格式

第一行是两个整数 n,m,具体含义见题目描述;

第二行 n 个整数表示每个烽火台的代价 ai。

输出格式

输出仅一个整数,表示最小代价。

数据范围

1≤m≤n≤2×1e5
0≤ai≤1000

输入样例:
5 3
1 2 5 6 2
输出样例:
4
思路:

动态规划+单调队列

f[i]表示第i个烽火台点燃需要的最小代价

维护单调性的步骤:
//维护单调性
while(hh<=tt && f[i]<=f[q[tt]])tt--;
代码:
#include<bits/stdc++.h>using namespace std;const int N=2e5+10;int n,m; int a[N];int q[N];int hh,tt=-1;
//dp
int f[N];int main()
{cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];}//前0个烽火台的最小代价是0 f[++tt]=0;int res=1e9;for(int i=1;i<=n;i++){//f[i]表示第i个烽火台点燃需要的最小代价//超出i-m范围则不合法 if(q[hh]<i-m)hh++;f[i]=f[q[hh]]+a[i];//维护单调性while(hh<=tt && f[i]<=f[q[tt]])tt--;q[++tt]=i; }for(int i=n-m+1;i<=n;i++)res=min(res,f[i]);cout<<res;return 0;
} 
/*
5 3
1 2 5 6 24*/

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

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

相关文章

vue组件如何使用?

今天我随便试两个组件 第一个轮播图 在minn.js 引入 import { createApp } from vue; import { Swipe, SwipeItem } from vant; const app createApp(); app.use(Swipe); app.use(SwipeItem); <van-swipe class"my-swipe" :autoplay"3000" indica…

python学习15:python中的input语句

python中的input语句 我们前面学习过print语句&#xff0c;可以将内容输出到屏幕上&#xff1b;在python中&#xff0c;与之对应的还有一个input语句&#xff0c;用来获取键盘输入。 数据输出&#xff1a;print 数据输入&#xff1a;input 使用上也很简单&#xff1a; 使用inp…

jvm高级面试题-2024

说下对JVM内存模型的理解 JVM内存模型主要是指Java虚拟机在运行时所使用的内存结构。它主要包括堆、栈、方法区和程序计数器等部分。 堆是JVM中最大的一块内存区域&#xff0c;用于存储对象实例。一般通过new关键字创建的对象都存放在堆中&#xff0c;堆的大小可以通过启动参数…

http 超全状态码

HTTP&#xff08;超文本传输协议&#xff09;是一种用于传输超文本的应用层协议。在进行HTTP通信时&#xff0c;服务器会向客户端返回一个状态码&#xff0c;用于表示请求的处理结果。 状态码由3位数字组成&#xff0c;使用第1个数字表示响应的类别&#xff0c;一共5种&#x…

广告牌效果的C#实现

前言 这个效果是在以前的项目时候&#xff0c;特效那边想要一个广告牌效果但是我不懂什么是广告牌两个人沟通半天&#xff0c;才把东西做出来。 代码如下 using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine;[ExecuteInEdit…

视图的作用

目录 视图的作用 创建视图 为 scott 分配创建视图的权限 查询视图 复杂视图的创建 视图更新的限制问题 更新视图中数据的部门编号&#xff08;视图的存在条件&#xff09; 限制通过视图修改数据表内容 创建只读的视图 复杂视图创建 oracle从入门到总裁:​​​​​​h…

关于GPT-SoVITS语音合成的效果展示(西游之西天送葬团)

目录 使用效果总结合成效果展示 使用效果总结 使用的是2024年03月21日22点28分更新的版本。 使用起来很方便&#xff0c;从它“自带界面”这点就能看出&#xff0c;易于使用也是目的之一&#xff0c;而且从训练到推理的每个步骤都能在界面中完成。 集成了多个实用工具&#…

快速创建zookeeper集群

先说明&#xff0c;我很穷&#xff01;&#xff01;&#xff0c;开不了多个虚拟机&#xff0c;zookeeper集群的3个节点都放在同一个虚拟机&#xff0c;所以搭建是一个伪集群&#xff0c;因为一个服务器挂机&#xff0c;所有节点都会停止。工作实际情况安装到三个服务器&#xf…

Doris 数据集成 Catalog

Doris 数据集成 Catalog 多源数据目录(Multi-Catalog)功能,旨在能够更方便对接外部数据目录,以增强Doris的数据湖分析和联邦数据查询能力。 在之前的 Doris 版本中,用户数据只有两个层级:Database 和 Table。当我们需要连接一个外部数据目录时,我们只能在Database 或 …

3、RabbitMQ_工作模式

一、简单模式 简介 简单模式 HelloWorld。一个生产者、一个消费者&#xff0c;不需要设置交换机使用默认的交换机。 代码示例 生产者public class Producer {//队列名称private final static String QUEUE_NAME "hello";public static void main(String[] args)…

【Linux】文件查看命令(六)

文章目录 wc 命令grep命令&#xff08;常用&#xff09;more 命令cat 命令less 命令&#xff08;不常用&#xff09;head 命令&#xff08;不常用&#xff09;tail 命令&#xff08;不常用&#xff09; wc 命令 功能描述: wc 命令用于统计文件中的字节数、字数、行数等信息。 …

H5抓包——Android 使用电脑浏览器 DevTools调试WebView

H5抓包——Android 使用电脑浏览器 DevTools调试WebView 一、使用步骤 1、电脑通过数据线连接手机&#xff0c;开启USB调试&#xff08;打开手机开发者选项&#xff09; 2、打开待调试的H5 App&#xff0c;进入H5界面 3、打开电脑浏览器&#xff0c;调试界面入口 如果用ed…

第十一章:位运算符与位运算

文章目录 第十一章&#xff1a;位运算符与位运算1.按位与运算&#xff1a;&2.按位或运算&#xff1a;|3.按位异或运算&#xff1a;^4.取反运算符&#xff1a;~5.左移运算符&#xff1a;<<6.右移运算符&#xff1a;>>总结 第十一章&#xff1a;位运算符与位运算…

免费SSL证书和付费SSL证书的区别点

背景&#xff1a; 在了解免费SSL证书和付费SSL证书的区别之前&#xff0c;先带大家了解一下SSL证书的概念和作用。 SSL证书的概念&#xff1a; SSL证书就是基于http超文本传输协议的延伸&#xff0c;在http访问的基础上增加了一个文本传输加密的协议&#xff0c;由于http是明…

二分练习题——妮妮的月饼工厂

妮妮的月饼工厂 题目分析 这里出现了“最高高度”&#xff0c;那么可以考虑用二分去做。 第一阶段二段性分析 希望月饼的高度最大&#xff0c;而月饼的高度要满足一个条件&#xff0c;就是按照该高度切出来的月饼的块数应该大于等于K块。那么现在就是满足条件的最大值&…

【爬虫基础】第6讲 opener的使用

在爬虫中&#xff0c;opener是一个用来发送HTTP请求的对象。它可以用来模拟浏览器发送请求&#xff0c;包括设置请求头、处理Cookie等操作。使用opener可以实现一些高级功能&#xff0c;如模拟登录、处理验证码等。 方法1&#xff1a; from urllib.request import Request,bu…

ARMday7作业

实现三个按键的中断&#xff0c;现象和代码 do_ipr.c #include "stm32mp1xx_gic.h" #include "stm32mp1xx_exti.h" extern void printf(const char *fmt, ...); unsigned int i 0; void do_irq(void) {//获取要处理的中断的中断号unsigned int irqnoGI…

扩展wordpress回收站功能

1、自定义WordPress回收站删除时间 WordPress回收站里的内容&#xff0c;默认会在30天之后删除&#xff0c;逻辑非常简单&#xff0c;否则WordPress回收站会越来越臃肿。当然&#xff0c;如果你觉得30天太长&#xff0c;我们也可以自定义内容存放的时间&#xff0c;我们需要把…

StableDiffusion Web UI开启FP8,极大节约显存

升级了Pytorch后&#xff0c;StableDiffusion最新版本就可以有使用FP8的基础了&#xff0c;因此把秋叶的LINUX包也升级到了最新的版本。 升级Pytorch参考我的升级记录&#xff1a; ComfyUI SDWebUI升级pytorch随记-CSDN博客 然后下一步就是如何开启FP8了。与ComfyUI不同&…

Spring boot2.X 配置https

背景 最近项目组说要将 http 升级成 https 访问&#xff0c;证书也给到我们这边了&#xff0c;当然我们这边用的是个二级域名&#xff0c;采用的是通配符访问的方式&#xff0c;比如一级域名是这样&#xff08;com.chinaunicom.cn&#xff09;&#xff0c;我们的则是&#xff0…