第一次
[点这里到原题](https://www.acwing.com/problem/content/156/)
给定一个大小为n≤106的数组。
有一个大小为k的滑动窗口,它从数组的最左边移动到最右边。
您只能在窗口中看到k个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为[1 3 -1 -3 5 3 6 7],k为3。
窗口位置 最小值 最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7
您的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数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
#include<bits/stdc++.h>
using namespace std;
int a[1000010];
deque<int>up;
deque<int>down;
int minn[1000010],maxx[1000010];
int n,k;
int main()
{
memset(maxx,0,sizeof(maxx));
memset(minn,0,sizeof(minn));
memset(a,0,sizeof(a));
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
while(up.size()&&up.front()<=i-k)//超出范围就出队
up.pop_front();
while(down.size()&&down.front()<=i-k)//超出范围就出队
down.pop_front();
if(!up.size())//wrong
up.push_back(i);//没东西就进去
else{
while(up.size()&&a[i]<=a[up.back()])//维护队列单调性
up.pop_back();
up.push_back(i);
}
if(!down.size())
down.push_back(i);
else{
while(down.size()&&a[i]>=a[down.back()])
down.pop_back();
down.push_back(i);
}
if(i>=k)
{
maxx[i-k]=a[down.front()];
minn[i-k]=a[up.front()];
}
}
for(int i=0;i<=n-k;i++)
printf("%d ",minn[i]);
printf("
");
for(int i=0;i<=n-k;i++)
printf("%d ",maxx[i]);
return 0;
}
第二次 2020.09.22
这里是调试模板,可以看到队列的变化:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int a[N], q[N];
int hh = 0, tt = -1;
void out(){
// for(int i = 0; i < O1; i ++)
// printf("%d ", q[i]);
// puts("");
printf("当前队列
");
for(int i = hh; i <= tt; i ++)
printf("%d ", a[q[i]] );
puts("");
puts("");
}
int main()
{
freopen("ttt.in", "r", stdin);
freopen("ttt.out", "w", stdout);
int n, k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
if (hh <= tt && i - k + 1 > q[hh])
hh ++ ;
printf("当前队列
");
for(int i = hh; i <= tt; i ++)
printf("%d ", a[q[i]] );
puts("");
puts("");
while (hh <= tt && a[q[tt]] >= a[i]){
tt -- ;
printf("当前队列
");
for(int i = hh; i <= tt; i ++)
printf("%d ", a[q[i]] );
puts("");
puts("");
}
q[ ++ tt] = i;
printf("当前队列
");
for(int i = hh; i <= tt; i ++)
printf("%d ", a[q[i]] );
puts("");
puts("");
if (i >= k - 1)
printf("________%d
", a[q[hh]]);
}
puts("");
return 0;
}
答案(第二次)
#include <iostream>
using namespace std;
const int N = 1000010;
int a[N], q[N];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
//这里处理的是最小值
int hh = 0, tt = -1//头 和 尾
for (int i = 0; i < n; i ++ )
{
if (hh <= tt && i - k + 1 > q[hh] ) hh ++ ;
//这里是当队列里面还有元素 以及 头不在这个窗口范围的时候,头就往后走一位
while (hh <= tt && a[q[tt]] >= a[i] ) tt -- ;
//这里是要把队列中所有比 现在要插入的元素 大的元素出队
//然后就会发现 现在队列里的元素 正是当前状态(加入新元素)的单调队列
q[ ++ tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
//队头是最小的
}
puts("");
//这里处理的是最大值
hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
while (hh <= tt && a[q[tt]] <= a[i]) tt -- ;
q[ ++ tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
return 0;
}
第三次 2020.11.15
第三次刷题地址
传送到题目
思路
经典的单调队列问题,三刷了……
简单地说就死维护一个单调队列,每次输出队尾即可。
#include <iostream>
using namespace std;
const int N = 1000010;
int a[N], q[N];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
//这里处理的是最小值
int hh = 0, tt = -1//头 和 尾
for (int i = 0; i < n; i ++ )
{
if (hh <= tt && i - k + 1 > q[hh] ) hh ++ ;
//这里是当队列里面还有元素 以及 头不在这个窗口范围的时候,头就往后走一位
while (hh <= tt && a[q[tt]] >= a[i] ) tt -- ;
//这里是要把队列中所有比 现在要插入的元素 大的元素出队
//然后就会发现 现在队列里的元素 正是当前状态(加入新元素)的单调队列
q[ ++ tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
//队头是最小的
}
puts("");
//这里处理的是最大值
hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
while (hh <= tt && a[q[tt]] <= a[i]) tt -- ;
q[ ++ tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
return 0;
}
第四次(这次是用deque做哒!)
第四刷的博客链接
四刷了哦,滑动窗口这道题……
以下是四种定义deque的方法
// 直接定义
std::deque<int> first; // empty deque of ints
//填充数字
std::deque<int> second (4,100); // four ints with value 100
//The contents of second are: 100 100 100 100
//复制某双端队列的某一段
std::deque<int> third (second.begin(),second.end()); // iterating through second
//The contents of third are: 100 100 100 100
//或者直接复制下整个其它双端队列
std::deque<int> fourth (third); // a copy of third
//The contents of fourth are: 100 100 100 100
//或者复制某个数组
// the iterator constructor can be used to copy arrays:
int myints[] = {16,2,77,29};
std::deque<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );
//The contents of fifth are: 16 2 77 29
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
//不要给双端队列q定义大小,我也不清楚
deque<int>q;
int main(){
//个数,窗口大小
int n, k; cin >> n >> k;
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
//递增
//依次将数组元素入队
for(int i = 1; i <= n; i ++){
//首先是出队操作
while( !q.empty() && a[i] < a[q.back()]) q.pop_back();
q.push_back(i);
if(!q.empty() && q.front() < i - k + 1) q.pop_front();
if(!q.empty() && i >= k) printf("%d ", a[q.front()]);
}
puts("");
//递减
q.clear();
for(int i = 1; i <= n; i ++){
//首先是出队操作
while( !q.empty() && a[i] > a[q.back()]) q.pop_back();
q.push_back(i);
if(!q.empty() && q.front() < i - k + 1) q.pop_front();
if(!q.empty() && i >= k) printf("%d ", a[q.front()]);
}
puts("");
return 0;
}
第五次
这次是在周测10做的,只用到单调递减队列。
题目 : 扫描
#include<bits/stdc++.h>
using namespace std;
const int N = 3e6 + 10;
deque<int>q;
int a[N];
int main(){
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++){
//首先当然是维护队列的单调性啦!
while(!q.empty() && a[i] > a[q.back()]) q.pop_back();
if(!q.empty() && q.front() < i - k + 1) q.pop_front();
q.push_back(i);
if(i >= k) printf("%d
", a[q.front()] );
}
return 0;
}