蓝桥杯训练-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}中最小的两个数,设为pa和pb,将pa和pb从{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。
输入格式
输入的第一行包含一个正整数n(n<=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;
}