题意:给你n个数字,现在有一个长度为k窗口,每次往右移动一位,问窗口中的最小值和最大值;
思路:滑动窗口最值问题,用单调队列解。也可以用RMQ和线段树过。
坑点:G++可能会超时,C++能过。
单调队列:
//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 1000005;
int n, k, a[MAXN], Max[MAXN], Min[MAXN];
int deq_Min[MAXN], deq_Max[MAXN], front1, rear1, front2, rear2;
int main()
{
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
front1 = rear1 = front2 = rear2 = 0;
for (int i = 0; i < n; i++)
{
//最小值
if (deq_Min[front1] == i-k) front1++;
while (front1 < rear1 && a[deq_Min[rear1-1]] >= a[i]) rear1--;
deq_Min[rear1++] = i;
Min[i] = a[deq_Min[front1]];
//最大值
if (deq_Max[front2] == i-k) front2++;
while (front2 < rear2 && a[deq_Max[rear2-1]] <= a[i]) rear2--;
deq_Max[rear2++] = i;
Max[i] = a[deq_Max[front2]];
}
for (int i = k-1; i < n; i++) printf("%d ", Min[i]); printf("\n");
for (int i = k-1; i < n; i++) printf("%d ", Max[i]); printf("\n");
return 0;
}
/*
8 3
1 3 -1 -3 5 3 6 7
*/
RMQ:
如果直接用RMQ的话会MLE(超内存),因为本题的查询长度恒为k,我们只需要保存 j*2<k 时最大那个j的即可,数组就不必要开那么大了,开成一维,递推到上面所说的 j 的情况停止。
//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 1000005;
int n, k, j, ST_Max[MAXN], ST_Min[MAXN];
int main()
{
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++)
{
int t; scanf("%d", &t);
ST_Min[i] = ST_Max[i] = t;
}
//RMQ
for (j = 1; j*2 < k; j *= 2)
{
for (int i = 0; i+j < n; i++)
{
ST_Min[i] = min(ST_Min[i], ST_Min[i+j]);
ST_Max[i] = max(ST_Max[i], ST_Max[i+j]);
}
}
for (int i = 0; i+k <= n; i++) printf("%d ", min(ST_Min[i], ST_Min[i+k-j])); printf("\n");
for (int i = 0; i+k <= n; i++) printf("%d ", max(ST_Max[i], ST_Max[i+k-j])); printf("\n");
return 0;
}
/*
8 3
1 3 -1 -3 5 3 6 7
*/
线段树:
//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 1000005;
const int MaxNode = MAXN * 4;
const int INF = 1000000005;
int n, k, ans1[MAXN], ans2[MAXN];
struct IntervalTree
{
int minv[MaxNode], maxv[MaxNode];
int pos, v;
int qL, qR, _min, _max;
void maintain(int o, int L, int R)
{
int lc = o*2, rc = o*2+1;
if (R > L)
{
minv[o] = min(minv[lc], minv[rc]);
maxv[o] = max(maxv[lc], maxv[rc]);
}
}
void update(int o, int L, int R)
{
if (L == R)
{
minv[o] = maxv[o] = v;
return ;
}
int lc = o*2, rc = o*2+1, M = L + (R-L)/2;
if (pos <= M) update(lc, L, M);
if (pos > M) update(rc, M+1, R);
maintain(o, L, R);
}
void query(int o, int L, int R)
{
if (qL <= L && R <= qR)
{
_min = min(_min, minv[o]);
_max = max(_max, maxv[o]);
return ;
}
int lc = o*2, rc = o*2+1, M = L + (R-L)/2;
if (qL <= M) query(lc, L, M);
if (qR > M) query(rc, M+1, R);
}
};
IntervalTree tree;
int main()
{
scanf("%d%d", &n, &k);
{
for (int i = 1; i <= n; i++)
{
int t; scanf("%d", &t);
tree.pos = i; tree.v = t;
tree.update(1, 1, n);
}
for (int i = 1; i+k-1 <= n; i++)
{
tree.qL = i; tree.qR = i+k-1;
tree._min = INF; tree._max = -INF;
tree.query(1, 1, n);
ans1[i] = tree._min; ans2[i] = tree._max;
}
for (int i = 1; i+k-1 <= n; i++) printf("%d ", ans1[i]); printf("\n");
for (int i = 1; i+k-1 <= n; i++) printf("%d ", ans2[i]); printf("\n");
}
return 0;
}
/*
8 3
1 3 -1 -3 5 3 6 7
*/
超时的set方法(感觉不应该超时的啊):
//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <set>
using namespace std;
const int MAXN = 1000005;
int n, k, a[MAXN], ans1[MAXN], ans2[MAXN];
multiset<int> s;
int main()
{
scanf("%d%d", &n, &k);
{
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
if (i < k) s.insert(a[i]);
}
for (int i = 0; i+k-1 < n; i++)
{
ans1[i] = *s.begin(); ans2[i] = *s.rbegin();
s.erase(a[i]);
s.insert(a[i+k]);
}
for (int i = 0; i+k-1 < n; i++) printf("%d ", ans1[i]); printf("\n");
for (int i = 0; i+k-1 < n; i++) printf("%d ", ans2[i]); printf("\n");
}
return 0;
}
/*
8 3
1 3 -1 -3 5 3 6 7
*/