标题:日志统计
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有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
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
分析:
1.题目中总共有5个变量:
- 日志的行数:N
- 时间跨度:D
- 需要的赞的最小值:K
- 帖子的时刻:ts
- 帖子的编号:id
2.题目中的时刻和编号的关系
-
一个时刻对应一个编号
-
一个编号对应多个时刻
-
即 编号 :时刻 = 1 : n
-
输入是连续性输入的
即,要使用数组方法:
1. 网络上大多都用的尺取法进行解题,这一种是经过求证的,是正确的解法
尺取法就是类似与用一把10cm的尺子,量一个大于10cm的线,先量前10cm,再在线的 10cm处用尺子的0刻度进行量,直到得出线的长度,但是这根线本身是有顺序的。(了解尺取法的,就略过博主的粗浅理解吧,看不懂的,百度搜一下,我的理解太浅薄了)
2. 其它方法。(我主要介绍的是我自己摸索出来的方法,利用两层排序,再进行比较。)
注:我不知道网上是否有与我一样的方法,如有相似的,见谅。
大致思路
- 将编号:id和时刻:ts,放入到结构体中
- 创建一个结构体数组
- 然后进行数据的输入。
- 对编号:id进行一次排序 —— 编号:id移动的同时,时刻:ts也需要跟着移动。
注:这里的排序算法需要自己进行手写,不能使用 < algorithm > 库的sort函数。 - 排序完成后再对不同的编号:id的内部时刻:ts进行排序,
如编号:id为1时,时刻的顺序:3 2 1,此时就需要进行一次排序(此时仍需要手写,不过有了之前的一个手写排序,只需要复制就好了) - (注:5,6放在同一循环体当中进行) 进行显示,此时先经历一次判断,即判断编号:id的时刻:ts个数是否小于热帖所需的赞的最小值K。如果大于或等于,就进入显示代码块的循环判断。最后将满足条件的编号:id显示出来
#include <iostream>
using namespace std;
//N行、时间跨度D、不少于K个赞、
//格式:ts id
//输入格式:N D K
// ts id
//ts->时刻
//依据id进行排序,最后再检验时间的跨度
struct imp{
long long int ts;//时刻
long long int id;//id//
};
//快速排序算法
void quickSort(imp zan[],int begin,int end)
{
if(begin < end){
imp za=zan[begin];
int i = begin;
int j = end;
while(i<j){
while(i<j && zan[j].id >= za.id)j--;//从右向左
if(i<j){
zan[i++] = zan[j];
}
while(i<j && zan[i].id < za.id)i++;//从左向右
if(i<j){
zan[j--] = zan[i];
}
}
zan[i] = za;
quickSort(zan,begin,i-1);//左边
quickSort(zan,i+1,end);//右边
}
}
void quickSort_1(imp s[], int l, int r)
{
if (l< r)
{
int i = l, j = r;
imp x = s[l];
while (i < j)
{
while(i < j && s[j].ts>= x.ts) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];
while(i < j && s[i].ts< x.ts) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quickSort_1(s, l, i - 1); // 递归调用
quickSort_1(s, i + 1, r);
}
}
int main(int argc, char *argv[]) {
int n,d,k,n_1,dz;
cin>>n>>d>>k;//n行、时间跨度D、不少于K个赞
//id数<n
imp zan[n];
for(int i=0;i<n;++i){
cin>>zan[i].ts>>zan[i].id;
}
quickSort(zan,0,n-1);
dz = 0;//记录循环
int tmp = 0; //记录各个id的首地址
while(dz<n){//内部排序的同时,进行显示
int za = 1;//记录赞
for(n_1 = 1;/*记录有多少个同类项dz<n*/ zan[dz].id == zan[dz+1].id; ++dz){
n_1++;
}
dz++;
quickSort_1(zan,tmp,dz-1);//内部排序
//开始显示
if(n_1 >= k){
for(int i=tmp; i < tmp + n_1; ++i){
for(int j = i+1; j<tmp + n_1; ++j){
if(zan[i].ts<= zan[j].ts && zan[j].ts < zan[i].ts + d && za<k/*时间跨度*/ ){
za++;
}
}
if(za>=k) { //如果赞的个数已经大于了 最小值,就直接跳出函数
cout << zan[i].id <<endl;
break;
}
}
}
tmp = tmp + n_1;
}
return 0;
}
反思:
其实我认为,可以一开始在结构体中,将编号和时刻都设为数组,最后对时刻的排序可以直接使用sort()函数进行排序,但我又认为不设置为数组可以少浪费内存空间。最后我权衡利弊,还是不讲编号和时刻设置为数组,而是使用了结构体数组,虽然要写两个排序函数,但实际写一遍就可以了,直接将第一次的复制,然后改几个地方就好了。
注明:
- 我写的这个方法不一定是对的,因为我不知道具体的测试数据,和不知道我所用的测试时间,所以我这段代码仅供参考。如果要正确答案,请用大神们的解法。
- 如有错误,欢迎指正。或者有更优化的算法,也请分享。谢谢。