【蓝桥杯】日志统计 - Java版解析

问题

题目描述

小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 N 行。

其中每一行的格式是:

ts id

表示在 ts 时刻编号 id 的帖子收到一个”赞”。

现在小明想统计有哪些帖子曾经是”热帖”。

如果一个帖子曾在任意一个长度为 D 的时间段内收到不少于 K 个赞,小明就认为这个帖子曾是”热帖”。

具体来说,如果存在某个时刻 T 满足该帖在 [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K 个赞,该帖就曾是”热帖”。

给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。

输入格式

第一行包含三个整数 N,D,K。

以下 N 行每行一条日志,包含两个整数 ts 和 id。

输出格式

按从小到大的顺序输出热帖 id。

每个 id 占一行。

数据范围

1≤K≤N≤10E5,
0≤ts,id≤10E5,
1≤D≤10000

输入样例:

7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3

输出样例:

1
3

第九届蓝桥杯省赛C++B组,第九届蓝桥杯省赛JAVAB组

解析

尺取法

顾名思义,就是像一把尺子一样,在变换尺子的长度中,一段段的截出满足条件的序列,然后进行相应的操作。

简介

尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。

尺取法比直接暴力枚举区间效率高很多,尤其是数据量大的时候,所以说尺取法是一种高效的枚举区间的方法,是一种技巧,一般用于求取有一定限制的区间个数或最短的区间等等。

当然任何技巧都存在其不足的地方,有些情况下尺取法不可行,无法得出正确答案,所以要先判断是否可以使用尺取法再进行计算。

适用条件

尺取法一般适用于在连续区间内求解问题,例如连续之和之类的。通过不断地变换尺子的长度,来选择最优的满足条件的序列。

解题步骤

  1. 选取相应的左右端点并初始化
  2. 推进右端点直到满足条件
  3. 判断当前的序列是否满足条件,不满足则跳出
  4. 更新相应的答案
  5. 左端点推进一步,缩小尺子范围
  6. 重复2~6步骤,直至跳出循环

代码

import java.util.*;
/**
 * 日志类
 */
class Log implements Comparable<Log>{
    int ts;//时刻
    int id;//编号
    public Log(int ts,int id) {
        this.ts =ts;
        this.id=id;
    }
    public int compareTo(Log o) {
        return Integer.compare(ts, o.ts);//根据初始时间大小排序
    }

}

/**
 * 日志统计:代码实现
 *
 * 查找一段时间区间获赞最多的id
 */
public class logStatistics {

    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt(), //行数
            d=scanner.nextInt(), //时间区间
            k=scanner.nextInt(); // 满足赞数
        int N=100010; // 极值
        Log[] logs=new Log[N];//日志列表
        int[] cnt=new int[N];//记录当前时间段各id的点赞数
        boolean[] isOK=new boolean[N];//记录满足点赞要求的id
        for(int i=0;i<n;i++) {
            int ts=scanner.nextInt(), //ts时刻
                id=scanner.nextInt(); //编号id
            logs[i]=new Log(ts, id);
        }
        Arrays.sort(logs,0,n);//让日志根据时间从小到大排列
        for(int i=0,j=0;i<n;i++) {
            int id=logs[i].id;
            cnt[id]++;//该条日志的id点赞加一
            while(logs[i].ts -logs[j].ts >=d) {//i走在前面,j走在后面,如果第i条日志的时间与第j条日志的时间差大于d,则j往前走一步
                cnt[logs[j].id]--;//原先的日志j就不包含在时间段内,则该id点赞数减一
                j++;//往前走一步
            }
            if(cnt[id]>=k) {
                isOK[id]=true;//若j~i时间段,id获赞总数满足要求
            }
        }
        //按从小到大的顺序输出热帖 id
        for(int i=1;i<N;i++) {//id从小到大遍历
            if(isOK[i])
                System.out.println(i);
        }

    }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫巳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值