AcWing 154. 滑动窗口 && AcWing 1238. 日志统计
滑动窗口
一般使用队列或者双指针解决。
本质是限定长度的队列,利用先进先出的特性,将过早的信息排除,只考虑近期信息。
当队未满时,将符合条件的元素入队;达到最大长度开始出队。
对于存在映射关系的元素,单纯利用queue可能较难实现,这时可以采用双指针。
队列:154. 滑动窗口 - AcWing
题面:
给定一个大小为 \(n≤10^6\) 的数组。
有一个大小为 \(k\) 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 \(k\) 个数字。
每次滑动窗口向右移动一个位置。
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int a[N], n, k;
int main()
{
deque<int> q; //双端队列
cin >> n >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
//先求最小值,即单增队列
for (int i = 1; i <= n; i++) {
//当队尾元素大于当前值时,因为窗口从左往右滑动,队尾不可能成为最小值,故出队
while (q.size() && q.back() > a[i])
q.pop_back();
q.push_back(a[i]);
//当队元素满k个或队头在k个数之前,即队头元素在窗口外,队头出队
if (i - k >= 1 && q.front() == a[i - k])
q.pop_front();
//前三个数已经被输入,窗口形成,开始输出队头对应的值
if (i >= k)
cout << q.front() << " ";
}
q.clear();
cout << endl;
//最大值同理
for (int i = 1; i <= n; i++) {
while (q.size() && q.back() < a[i])
q.pop_back();
q.push_back(a[i]);
if (i - k >= 0 && q.front() == a[i - k])
q.pop_front();
if (i >= k)
cout << q.front() << " ";
}
}
双指针:1238. 日志统计 - AcWing
题面:
对于一份帖子,如果存在某个时刻 \(T\) 满足该帖在 \([T,T+D)\) 这段时间内收到不少于 \(K\) 个赞,该帖就曾是”热帖”。日志共有 \(N\) 行,格式为ts id,表示在 \(ts\) 时刻编号 \(id\) 的帖子收到一个“赞”。
给定日志,请你统计出所有曾是”热帖”的帖子编号。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 5;
int n, d, k;
int cnt[N]; //存储符合条件的点赞数
set<int> res;
PII logs[N]; //存储点赞时间与被赞帖子的映射关系
int main()
{
cin >> n >> d >> k;
for (int i = 0; i < n; i++)
cin >> logs[i].first >> logs[i].second;
sort(logs, logs + n); //默认按照时间排序
for (int i = 0, j = 0; i < n; i++) {
int id = logs[i].second;
cnt[id]++;
while (logs[i].first - logs[j].first >= d)
//排除不再合规的早期点赞数,同时移动慢指针(即窗口左侧)
cnt[logs[j++].second]--;
//直接输出的话可能输出多次,故使用set自动排序去重
if (cnt[id] >= k) res.insert(id);
}
for (auto i : res) cout << i << endl;
}

浙公网安备 33010602011771号