单调队列 滑动窗口;

 

最近做了一个滑动窗口得题;然后就找一些来做了;

套着模板做勒,不容易啊;

看别人的解释套用一下

 

那么如何用队列处理呢?先简单说一下解题思路,以求最小值为例:一开始数组下标i为0,我们向队列里不停的放元素,并且一直保持队首位元素为最小值,直到第k个数,此时队首元素即为前k个数的最小值。然后我们把队首输出。继续往下走,走的过程中把队列中下标超出(i-k+1)~i区间范围的数踢掉,继续保持队首元素为区间内最小值,然后输出队首元素。

简单归纳一下,对于每一次循环,我们要做的就是:先踢掉超出区间范围的元素,放入元素并保证队首为数组中当前区间的最小值,输出队首,往复。队列内储存的就是放入元素之前区间内单调递增的最小值序列。

那么我们要解决的问题有两个:

1. 如何去除超出区间范围的元素:这就是代码中node节点的作用了,node节点中储存了原数组的下标,对于每一次新输入的第i个数,因为单调队列内元素位置是从前到后的,我们只要将队首元素的node.order和i进行判断,看此时是否i-node.order>=k即可,是就让队首元素出列。

2. 如何保证队首元素为区间内最小值:对于某一区间,我们每输入一个数,就看看队尾元素的值是否比它大,是就让这个元素出列,直到队列为空或者队尾元素值比这个数小,然后我们把这个数放进去,这个时候队首元素即为区间最小值,输出即可。

合理性分析:从队列为空开始,我们放入第0个数,一直到k-1,每一次输入的时候都让队尾值比当前输入值小的出列,很容易知道此时队首就是[0,k-1]的最小值。那么接下来输入第k个数,可以肯定的是区间[1,k]的最小值与[0,k-1]的最小值有关。有两种情况,一是[1,k]的最小值来自k前面的数,这种情况下,我们看[0,k-1]的情况,若[0,k-1]的最小值来自第0位,就说明队列里肯定有除第0个元素外的元素,即队列里肯定有[1,k-1]的最小值序列,且它们是单调递增的。此时由于第0位超出区间范围,出列,不影响区间[1,k]最小值的查找。若[0,k-1]的最小值来自[1,k-1],自然不影响[1,k]最小值的查找。二是最小值就是k,那么进行操作即可把队列清空,再把它放进即可,此时队首就是k,即最小值。对于一般性的情况,同样有上述关系。

要注意的点:

1. 写if和while的时候一定要先写判断队列是否为空,否则if和while中会进行溢出操作。

2. 判断队首是否出队和队列长度没有确定关系,因为队列不一定都包含了整个区间的元素。

3. 对于重复元素去不去除都可以,去除可以保证队列中至少有一个,不去除不影响队首,且在遇到更小的值时都会出队。

4. 这个算法只有一个for循环,且每个元素都只出入一次,所以时间复杂度为o(n)。 
————————————————
版权声明:本文为CSDN博主「Gaoithe」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_40471574/article/details/90577147

P1440 求m区间内的最小值

题目描述

一个含有 nn 项的数列,求出每一项前的 mm 个数到它这个区间内的最小值。若前面的数不足 mm 项则从第 11 个数开始,若前面没有数则输出 00。

输入格式

第一行两个整数,分别表示 nn,mm。

第二行,nn 个正整数,为所给定的数列 a_iai​。

输出格式

nn 行,每行一个整数,第 ii 个数为序列中 a_iai​ 之前 mm 个数的最小值。

输入输出样例

输入 #1

6 2
7 8 1 4 3 2

输出 #1

0
7
7
1
1
3 

说明/提示

对于 100\%100% 的数据,保证 1\le m\le n\le2\times10^61≤m≤n≤2×106,1\le a_i\le3\times10^71≤ai​≤3×107。

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int id;
	int va;
}a[1000005];
deque<node>q;
int main()
{
	int t;
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&t);
		if(i==0)
		cout<<0<<endl;
		else
		printf("%d\n",q.front().va);
		if(!q.empty()&&i-q.front().id>=m)
		q.pop_front();
		while(!q.empty()&&q.back().va>t)
		q.pop_back();
		a[i].id=i;
		a[i].va=t;
		q.push_back(a[i]);
	}
}

之前牛客的滑动窗口

https://blog.csdn.net/qq_49638570/article/details/115480354

又找过了一个模板

滑动窗口
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
给一个长度为N的数组,一个长为K的滑动窗体从最左端移至最右端,你只能看到窗口中的K个数,每次窗体向右移动一位,如下图:

你的任务是找出窗体在各个位置时的最大值和最小值。
输入描述:
第1行:两个整数N和K;
第2行:N个整数,表示数组的N个元素(≤2 \times10^9≤2×10
9
);
输出描述:
第一行为滑动窗口从左向右移动到每个位置时的最小值,每个数之间用一个空格分开;
第二行为滑动窗口从左向右移动到每个位置时的最大值,每个数之间用一个空格分开。
示例1
输入


8 3
1 3 -1 -3 5 3 6 7

输出 


-1 -3 -3 -3 3 3
3 3 5 5 6 7

 


备注:
对于20 %20%的数据,K≤N≤1000;
对于50 %50%的数据,K≤N≤10^5K≤N≤10^5;
对于100 %100%的数据,K≤N≤10^6K≤N≤10^6。

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int id;
	int va;
}a[1000005];
int c[1000005];
deque<node>q,p;
int main()
{
	int k=0;
	int n,m;
	scanf("%d%d",&n,&m);
	int t;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&t);
		if(!q.empty()&&i-q.front().id>=m)
		q.pop_front();
		if(!p.empty()&&i-p.front().id>=m)
		p.pop_front();
		while(!q.empty()&&t>q.back().va)
		q.pop_back();
		while(!p.empty()&&t<p.back().va)
		p.pop_back();
		a[i].id=i;
		a[i].va=t;
		q.push_back(a[i]);
		p.push_back(a[i]);
		if(i>=m-1)
		{
			c[k++]=q.front().va;
			printf("%d ",p.front().va);
		}
	}
	cout<<endl;
	for(int i=0;i<k;i++)
	{
		printf("%d ",c[i]);
	}
}

 

P2032 扫描

题目描述

有一个 1 \times n1×n 的矩阵,有 nn 个整数。

现在给你一个可以盖住连续 kk 个数的木板。

一开始木板盖住了矩阵的第 1 \sim k1∼k 个数,每次将木板向右移动一个单位,直到右端与第 nn 个数重合。

每次移动前输出被覆盖住的数字中最大的数是多少。

输入格式

第一行两个整数 n,kn,k,表示共有 nn 个数,木板可以盖住 kk 个数。

第二行 nn 个整数,表示矩阵中的元素。

输出格式

共 n - k + 1n−k+1 行,每行一个整数。

第 ii 行表示第 i \sim i + k - 1i∼i+k−1 个数中最大值是多少。

输入输出样例

输入 #1

5 3
1 5 3 4 2

输出 #1

5
5
4

说明/提示

对于 20\%20% 的数据,1 \leq k \leq n \leq 10^31≤k≤n≤103。

对于 50\%50% 的数据,1 \leq k \leq n \leq 10^41≤k≤n≤104。

对于 100\%100% 的数据,1 \leq k \leq n \leq 2 \times 10^61≤k≤n≤2×106,矩阵中的元素大小不超过 10^4104 并且均为正整数。

 

 

#include <bits/stdc++.h>
using namespace std;
struct node
{
	int id;
	int va;
}a[1000005];
deque<node>q;
int main()
{
	int t;
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&t);
		if(!q.empty()&&i-q.front().id>=m)
		{
			q.pop_front();
		}
		while(!q.empty()&&q.back().va<t)
		{
			q.pop_back();
		}
		a[i].id=i;
		a[i].va=t;
		q.push_back(a[i]);
		if(i>=m-1)
		{
			printf("%d\n",q.front().va);
		}
	}
}

 

题目描述

P1714 切蛋糕

 

今天是小Z的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值。

小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但小Z最多又只能吃M小块(M≤N)的蛋糕。

吃东西自然就不想思考了,于是小Z把这个任务扔给了学OI的你,请你帮他从这N小块中找出连续的k块蛋糕(k≤M),使得其上的幸运值最大。

输入格式

输入文件cake.in的第一行是两个整数N,M。分别代表共有N小块蛋糕,小Z最多只能吃M小块。

第二行用空格隔开的N个整数,第i个整数Pi代表第i小块蛋糕的幸运值。

输出格式

输出文件cake.out只有一行,一个整数,为小Z能够得到的最大幸运值。

输入输出样例

输入 #1

5 2
1 2 3 4 5

输出 #1

9

输入 #2

6 3
1 -2 3 -4 5 -6

输出 #2

5

说明/提示

对20%的数据,N≤100。

对100%的数据,N≤500000,|Pi|≤500。 答案保证在2^31-1之内。

#include<bits/stdc++.h>
using namespace std;
int b[1000005];
int sum[1000005];
struct node
{
	int id;
	int va;
}a[1000005];
deque<node>q;
int main()
{
	int ans;
	int n,m;
	scanf("%d%d",&n,&m);
	
	for(int i=0;i<n;i++)
	{
		scanf("%d",&b[i]);
		if(i==0)
		sum[0]=b[i];
		else
		sum[i]+=sum[i-1]+b[i];
		if(!q.empty()&&i-q.front().id>m)
		{
			q.pop_front();
		}
		while(!q.empty()&&q.front().va>sum[i])
		{
			q.pop_back();
		}
		a[i].id=i;
		a[i].va=sum[i];
		q.push_back(a[i]);
		if(i==0)
		{
			ans=sum[i];
		}
		else
		if(i<m)
		ans=max(ans,sum[i]);
		else
		ans=max(ans,sum[i]-q.front().va);
	}
	printf("%d\n",ans);
}

 

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值