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;
}
posted @ 2023-12-04 11:31  蒟蒻爬行中  阅读(33)  评论(0)    收藏  举报