蓝桥杯2018年第九届C++——日志统计,第八题

标题:日志统计

小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有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个变量:

  1. 日志的行数:N
  2. 时间跨度:D
  3. 需要的赞的最小值:K
  4. 帖子的时刻:ts
  5. 帖子的编号:id

2.题目中的时刻和编号的关系

  1. 一个时刻对应一个编号

  2. 一个编号对应多个时刻

  3. 编号 :时刻 = 1 : n

  4. 输入是连续性输入的
    即,要使用数组

    方法:

    1. 网络上大多都用的尺取法进行解题,这一种是经过求证的,是正确的解法
    尺取法就是类似与用一把10cm的尺子,量一个大于10cm的线,先量前10cm,再在线的 10cm处用尺子的0刻度进行量,直到得出线的长度,但是这根线本身是有顺序的。(了解尺取法的,就略过博主的粗浅理解吧,看不懂的,百度搜一下,我的理解太浅薄了)
    2. 其它方法。(我主要介绍的是我自己摸索出来的方法,利用两层排序,再进行比较。)
    注:我不知道网上是否有与我一样的方法,如有相似的,见谅。

大致思路

  1. 编号:id时刻:ts,放入到结构体
  2. 创建一个结构体数组
  3. 然后进行数据的输入。
  4. 编号:id进行一次排序 —— 编号:id移动的同时,时刻:ts也需要跟着移动。
    注:这里的排序算法需要自己进行手写,不能使用 < algorithm > 库的sort函数
  5. 排序完成后再对不同的编号:id的内部时刻:ts进行排序,
    如编号:id为1时,时刻的顺序:3 2 1,此时就需要进行一次排序(此时仍需要手写,不过有了之前的一个手写排序,只需要复制就好了)
  6. (注: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()函数进行排序,但我又认为不设置为数组可以少浪费内存空间。最后我权衡利弊,还是不讲编号和时刻设置为数组,而是使用了结构体数组,虽然要写两个排序函数,但实际写一遍就可以了,直接将第一次的复制,然后改几个地方就好了。

注明:

  1. 我写的这个方法不一定是对的,因为我不知道具体的测试数据,和不知道我所用的测试时间,所以我这段代码仅供参考。如果要正确答案,请用大神们的解法。
  2. 如有错误,欢迎指正。或者有更优化的算法,也请分享。谢谢。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值