题目描述
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。其中每一行的格式是:
ts id
表示在ts时刻编号id的帖子收到一个"赞"。
现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。
具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。
输入格式
第一行包含三个整数N、D和K。
以下N行每行一条日志,包含两个整数ts和id。
对于50%的数据,1 <= K <= N <= 1000
对于100%的数据,1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000
输出格式
按从小到大的顺序输出热帖id。每个id一行。
样例输入
7 10 2 0 1 0 10 10 10 10 1 9 1 100 3 100 3
样例输出
1 3
代码
代码贴在这里,其中有部分解释,但是我自己搞懂这个题的思想是通过代入实例实现的。
可以看一下为什么排序的解释,对后面理解算法的中心思想会有帮助;
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second
const int N=100010;
int n,d,k;
int cnt[N];//记录id出现次数
PII logs[N];//记录日志信息
bool st[N];
int main()
{
scanf("%d%d%d",&n,&d,&k);
for(int i=0;i<n;i++) scanf("%d%d",&logs[i].x,&logs[i].y);
sort(logs,logs+n);//给日志记录按照字典序排序
//这里排序的目的:方便我们将每一个日志记录的时刻看作一个实数轴上的有序的点,这样
//在下面用i,j指针遍历每一个日志时,就可以i,j指针相减得到的时刻,作为是否处于题干
//给定的有效时间段大小(具体逻辑根据下面的实例分析结合看)
for(int i=0,j=0;i<n;i++)//i,j指针用来指向日志
{
int t=logs[i].y;//t就是i指针指向的日志的id号;
cnt[t]++;//日志id号为t的数量加1;
//判断i,j指针括起来的时间段是否属于k-有效的时间范围
while(logs[i].x-logs[j].x>=d)
{
cnt[logs[j].y]--;
j++;
}
if(cnt[t]>=k) st[t]=true;
}
for(int i=0;i<=100000;i++)
if(st[i]==true)
cout << i << endl;
return 0;
}
实例代入
我们将题干中给定的实例代入代码中,观察算法的具体思路;
n = 7,d = 10,k=2
排序后的日志 <0 1> <0 10> <9 1> <10 1> <10 10> <100 3> <100 3>
双指针核心算法思想:
for(int i=0,j=0;i<n;i++)//i,j指针用来指向日志
{
int t=logs[i].y;//t就是i指针指向的日志的id号;
cnt[t]++;//日志id号为t的点赞数量加1;
while(logs[i].x-logs[j].x>=d)
{
cnt[logs[j].y]--;
j++;
}
if(cnt[t]>=k) st[t]=true;
}
i==0,j==0时,说明i,j指针都指向第一个日志,t=1,cn[1]=1,此时i,j指针括起来的范围仍然是小于10的。为有效范围,然后才可以判断其点赞数量符合标准;
i==1,j==0时,说明i指针往前走,指向第二个日志,j指针还是第一个日志, t=10,cnt[10]=1;此时i,j指针括起来的范围仍然是小于10为有效范围
i==2,j==0,t=1,cnt[1]=2; 符合标准且为热贴
i==3,j==0 t=1,cnt[1]=3;注意此时i,j指针括起来的范围已不是有效的范围了,j指针所指向的日志属于本次时间段统计点赞数是否合乎热帖标准之外的日志,将cnt[logs[j].y]--;j指针右移,再次while循环判断,如果满足i.j指针在同一时间段,才能进行是否为热帖的判断;、
下面步骤同上分析;
总结算法思想
总结:本题主要思想是双指针( i , j )算法来限定有效的时间段大小,如果在 i 指针往前走的过程中,其j作为起点,i 作为终点的时间段不符合有效时间段大小,必须去掉 j 指针指向的日志信息,往前挪动 j 指针,使其限定时间段符合有效时间段大小,才可以进行热帖的判断,也就是 id 是否热帖的判断必须在有效时间段内统计的 id 次数为真;
这就是我对这个题的一些理解,大家有补充的评论区留言,分享一下心得,指正一下错误,谢谢大家。