[蓝桥杯2018初赛]日志统计。 所用算法:双指针算法

1373: [蓝桥杯2018初赛]日志统计 <—做题链接

1373: [蓝桥杯2018初赛]日志统计
时间限制: 1 Sec  内存限制: 256 MB
提交: 249  解决: 73
[状态] [提交] [命题人:外部导入]
题目描述
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。 
其中每一行的格式是:ts id。表示在ts时刻编号id的帖子收到一个"赞"。   
现在小明想统计有哪些帖子曾经是"热帖"。 
如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。   
具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。  
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。  
输入
第一行包含三个整数N、D和K。  
以下N行每行一条日志,包含两个整数ts和id。 
1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000  
输出
按从小到大的顺序输出热帖id。每个id一行。
样例输入 Copy
7 10 2  
0 1  
0 10    
10 10  
10 1  
9 1
100 3  
100 3 
样例输出	Copy
1
3

经过仔细读题,我们发现这一题应当用双百算法来做
首先给出代码,代码里有解释

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

#define x first
#define y second

const int N = 100010;

int n, d, k;
PII logs[N];
bool st[N];//用来标记id号,因为id <= 1e5,所以可以利用遍历来输出。
int cnt[N];//用来记录一个id号获得的赞数,表示形式为cnt[id]++;

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);
    //排序时默认以first为准排序
    sort(logs, logs + n);
    
    //双指针算法, i走在前面,j走在后面
    for (int i = 0, j = 0; i < n; i ++ ){
        int t = logs[i].y;//t表示为i时刻的id号
        cnt[t] ++;//在j时刻,id号为t的大佬日记获得了一个赞,给t大佬加一分
        
        //注意这里问什么是大于等于d,因为条件里给的时间段是:[T,T+D)
        while (logs[i].x - logs[j].x >= d){//两个指针跨越的时间超过了d,早期的赞过期了
            cnt[logs[j].y] --;//就是这位大佬,获赞的时间太久远了,赞作废了,哭去吧
            j ++;//在logs[j].x时刻的太久远了,往前挪挪。
            //这个循环,直到最后一个赞不过期为止。
        }
        //记录热帖的id号,好知道谁才是大佬
        if (cnt[t] >= k) st[t] = true;
    }
    
    //遍历一遍id号,展现大佬
    for (int i = 0; i <= 100000; i ++ ) if (st[i]) cout << i << endl;
    
    return 0;
}

接着我们在说一下这题的解题思路。
根据题目意思,我们要求的是所有在时间段小于d内,所有点赞数大于或等于k的id号
所以我们要做的就是在一个区间[T, T+D)内是否有一篇热文的点赞数是否已经超过了k,并且将这个区间,从头到尾检查一遍。

而我们要解决的问题有:
1.怎么标记所有的数据——————用pair<int,int>
2.怎么标记某位id的获赞篇数————用cnt标记
3.怎么标记符合条件的id号————用bool类型数组,st[]

还有一个问题,我们怎么将这个区间块从前向后挪,依次来检验所有的热吻
这就需要使用二分法了,利用两个指针,使指针指向的区间始终使小于d的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值