蓝桥杯训练-3.14

蓝桥杯训练-3.14

代码练习

• H 日志统计

Description

小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。其中每一行的格式是:

ts id

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

现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。

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

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

Input

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

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

对于50%的数据,1 <= K <= N <= 1000

对于100%的数据,1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000

Output

按从小到大的顺序输出热帖id。每个id一行。

Sample Input 1

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

Sample Output 1

1
3
思路:

开一个二维的动态数组,行是编号为id的日志,这一行中的所有元素就是这些编号为id被点赞的时刻ts,我们可以用t[x].size()来获知每一行的长度,也就是编号为id的这一行存了几个时间段,存了几个时间段就是获得了几次赞。将这一行时刻从小到大排序,然后用两个端点l,r来形成一个滑动窗口,看看当获赞次数满足(也就是>=k)的区间里面,它们的时刻是否也满足[T, T+D),如果不是,左端点l加一,右端点r加1,也就是向右平移,也就是滑动。

代码实现:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;//数据规模最大为100000,这里数组开到100005
int n, d, k;//n表示日志一共有n行,d是需要满足的时间段长度,k是不少于k个赞
vector<int> t[maxn];
int ans[maxn];
bool judge(int x)
{
    int len = t[x].size();//获得二维数组编号x行的长度,这个长度即为该日志被点赞了几次,也就是存入了多少个时刻
    if(len < k)
        return false;
    sort(t[x].begin(),t[x].end());//给二维数组编号x行的多个时刻进行从小到大排序
    int l = 0, r = 0, sum = 0;//滑动窗口的两端,也是尺取法的两头,sum统计目前l和r范围里面的获赞次数
    while(l <= r && r < len)
    {
        sum++;//开始时,范围里面就有一个赞,(0,0)第0个也是
        if(sum >= k)//当范围里面获赞次数大于等于k次时,才开始看看里面的时间间隔
        {
            if(t[x][r] - t[x][l] < d)
                return true;
            else
            {
                l++;//时间间隔不满足的话,左边的坐标往前提一个
                sum--;//自然获赞数也少一个
            }
        }
        r++;//最后右端点往前,自然下一次循环sum也多一个,正好对应上了
    }
    return false;
}
int main()
{
    cin >> n >> d >> k;
    for (int i = 1; i <= n; i++)
    {
        int ts, id;//表示ts时刻编号id的帖子收到一个赞
        cin >> ts >> id;
        t[id].push_back(ts);//这里将编号id在哪个时刻收到赞存入数组中
    }
    int cnt = 0;
    for(int i = 1; i < maxn; i++)//循环日志的编号
    {
        if(judge(i))
            ans[++cnt] = i;//如果符合要求,那么这个编号就曾是热帖,就存入数组
    }
    for(int i = 1; i <= cnt; i++)
    {
        cout << ans[i] << endl;;
    }
    return 0;
}

当中的循环也可以不设sum,直接r - l + 1来表示这个区间里面的获赞数,如下:

 while(l <= r && r < len)
    {
        if(r - l + 1 >= k)//当范围里面获赞次数大于等于k次时,才开始看看里面的时间间隔
        {
            if(t[x][r] - t[x][l] < d)
                return true;
            else
            {
                l++;//时间间隔不满足的话,左边的坐标往前提一个
            }
        }
        r++;//最后右端点往前,自然下一次循环sum也多一个,正好对应上了
    }
• Huffuman树

问题描述

Huffman树在编码中有着广泛的应用。在这里,我们只关心Huffman树的构造过程。
  给出一列数{pi}={p0, p1, …, pn-1},用这列数构造Huffman树的过程如下:

1. 找到{pi}中最小的两个数,设为papb,将papb从{pi}中删除掉,然后将它们的和加入到{pi}中。这个过程的费用记为pa + pb
  2. 重复步骤1,直到{pi}中只剩下一个数。
在上面的操作过程中,把所有的费用相加,就得到了构造Huffman树的总费用。
  本题任务:对于给定的一个数列,现在请你求出用该数列构造Huffman树的总费用。

例如,对于数列{pi}={5, 3, 8, 2, 9},Huffman树的构造过程如下:

1. 找到{5, 3, 8, 2, 9}中最小的两个数,分别是2和3,从{pi}中删除它们并将和5加入,得到{5, 8, 9, 5},费用为5。
  2. 找到{5, 8, 9, 5}中最小的两个数,分别是5和5,从{pi}中删除它们并将和10加入,得到{8, 9, 10},费用为10。
  3. 找到{8, 9, 10}中最小的两个数,分别是8和9,从{pi}中删除它们并将和17加入,得到{10, 17},费用为17。
  4. 找到{10, 17}中最小的两个数,分别是10和17,从{pi}中删除它们并将和27加入,得到{27},费用为27。
  5. 现在,数列中只剩下一个数27,构造过程结束,总费用为5+10+17+27=59。

输入格式

输入的第一行包含一个正整数nn<=100)。
  接下来是n个正整数,表示p0, p1, …, pn-1,每个数不超过1000。

输出格式

输出用这些数构造Huffman树的总费用。

样例输入

5
5 3 8 2 9

样例输出

59

思路:

因为这里涉及到删除,加入,第一时间想到的是动态数组的应用,我们可以开一个动态数组,然后用数组长度作为循环条件,每一次都对数组中的数据,进行从大到小排列(因为动态数组的加入和删除都是从末尾进行的),每一次取末尾的两个数加起来,然后累加进总和sum里面,然后把最后两个数据删除,再加入俩个的和,重复循环,直到数组中只剩下一个数。

代码实现:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 105;
vector<int> t;
int a[maxn];
int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        t.push_back(a[i]);
    }
    int sum = 0;
    int num = n - 1;
    while(num > 0)
    {
        sort(t.begin(), t.end(), greater<int>());
        int x = t[num] + t[num - 1];
        sum += x;
        t.pop_back();
        t.pop_back();
        t.push_back(x);
        num--;
    }
    cout << sum << endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值