蓝桥杯每日练习

本文介绍了蓝桥杯编程竞赛中的几道题目,包括扑克牌的巧妙排列、质数拆分问题、日志统计分析、递增三元组计数以及外卖店优先级计算。通过解析解题报告,展示了如何利用栈模拟、01背包动态规划、事件处理和二分查找等算法解决这些问题。
摘要由CSDN通过智能技术生成

蓝桥杯冲冲冲~

回顾

🔖蓝桥杯每日练习(猴子分香蕉,等差数列,平方序列,倍数问题)


🔖蓝桥杯每日练习(纯质数(筛法的应用),最少砝码,灌溉)


🔖蓝桥杯每日练习(年龄巧合,纸牌三角形,取球游戏)


🚀今日题目

💓巧排扑克牌

栈模拟
题目描述

小明刚上小学,学会了第一个扑克牌“魔术”,到处给人表演。魔术的内容是这样的:

他手里握着一叠扑克牌:A,2,…J,Q,K一共 13张。他先自己精心设计它们的顺序,然后正面朝下拿着,开始表演。

只见他先从最下面拿一张放到最上面,再从最下面拿一张翻开放桌子上,是 A;然后再从最下面拿一张放到最上面,再从最下面拿一张翻开放桌子上,是 2;…如此循环直到手中只有一张牌,翻开放桌子上,刚好是 K。

这时,桌上牌的顺序是:A,2,3,4,5,6,7,8,9,10,J,Q,K。

请你计算一下,小明最开始的时候手里牌的顺序是怎样的。

把结果写出来,逗号分割,小明“魔术”开始时,最下面的那张牌输出为第一个数据。
原题传送门

解题报告

因为他是填空题,所以我们可以直接手算。具体怎么算呢,我们读题之后抽出有用的信息,可以知道他的放牌方式是隔一张放一张,所以我们就可以模拟了。
第一次直接排到最后:x1x2x3x4x5x6x(x表示空的位置),那么表示我们接下来可以用的位置是第1,3,5,7,9,11,13,所以第二次就直接隔一个来填充空位,以此类推直到填充完13个位置。每次找空位置的方法,其实就是数据结构中的栈。

参考代码(C++版本)
#include <iostream>
#include <string>
using namespace std;
int main()
{
  cout<<"7, A, Q, 2, 8, 3, J, 4, 9, 5, K, 6, 10";
  return 0;
}

🌟质数拆分

🌱题目描述

将 2019 拆分为若干个两两不同的质数之和,一共有多少种不同的方法?
注意交换顺序视为同一种方法,例如 2 + 2017 = 2019 与 2017 + 2 = 2019视为同一种方法。
原题传送门

🌴解题报告

先用各种筛法把1~2019之间的素数筛出来(当然用什么筛法都可以),然后问题就转化成了一个01背包。对于每一个素数我们要考虑的是放与不放,计算出能加到每一个数字的方案数。我们用dp[i]来表示和是i的方案数,加上不放当前素数的方案数来转移,就可以求出答案。就得要初始化dp[0]=1(什么也不放就能达到0)。

🌵参考代码(C++版本)
#include <iostream>
using namespace std;
#define int long long
const int N=2020;
int primes[N];
int idx;
int st[N];
int d[N];
void prime() {
  for(int i=2;i<=2019;i++) {
    if(!st[i]) primes[idx++]=i;
    for(int j=0;j<idx&&primes[j]*i<=2019;j++) {
      st[primes[j]*i]=1;
      if(i%primes[j]==0) break;
    }
  }
}
signed main()
{
  prime();
  d[0]=1;
  for(int i=0;i<idx;i++) {
    for(int j=2019;j>=primes[i];j--) {
      d[j]+=d[j-primes[i]];
    }
  }
  cout<<d[2019];
  return 0;
}

日志统计

题目描述

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

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

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

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

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

🚗解题报告

每一个事件(称关于点赞的描述为事件)都是属于一个人的,所以我们可以按照每个人的编号把事件进行分类。分属到每个人后我们排序就可以得到按照时间顺序的描述了。我们枚举每个人的编号,然后枚举属于他的事件中k个为一组,看每组的时间差是不是在d以内。如果满足条件,我们就可以直接输出编号,开始对下一个人的检查。这样做乍一看两层循环时间复杂度是 n 2 n^2 n2的,但是仔细一想就会发现一个事件只会属于一个人,所以每一个事件最多只会被遍历到一次。

参考代码(C++版本)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
vector<int>res[N];
int n, d, k;
int main()
{
    cin >> n >> d >> k;
    for (int i = 0; i < n; i++) {
        int a, b;
        cin >> a >> b;
        res[b].push_back(a);
    }
    for(int i = 0;i < N; i++) sort(res[i].begin(),res[i].end());
    for (int i = 0; i < N; i++) {
        if (res[i].size()<k) continue;
        for (int j = 0; j < res[i].size() - k + 1; j++) {
            int end = j + k - 1;
            if (res[i][end] - res[i][j] < d) {
                cout << i << endl;
                break;
            }
        }
    }
    return 0;
}

递增三元组

题目描述

给定三个整数数组
  A = [A1, A2, … AN],
  B = [B1, B2, … BN],
  C = [C1, C2, … CN],
  请你统计有多少个三元组(i, j, k) 满足:
  1. 1 <= i, j, k <= N
  2. Ai < Bj < Ck
原题传送门

解题报告

要使三个数满足 Ai < Bj < Ck,我们只需要枚举第二个数字x,找到第一个数组中比x小的数的数量a,和第三个数组中比x大的数的数量b,然后用a*b就是x能得到的三元组的数组。找的过程我们可以先排序,然后用二分查找的方式来找到对应位置。

参考代码(C++版本)
#include <iostream>
#include <vector>
#include <algorithm>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N], c[N];
int n;
signed main()
{
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++) cin >> b[i];
    for (int i = 0; i < n; i++) cin >> c[i];
    sort(a, a + n), sort(b, b + n), sort(c, c + n);
    int ans = 0;
    for (int i = 0; i < n; i++) {
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r + 1>> 1;
            if (a[mid] < b[i]) l = mid;
            else r = mid - 1;
        }
        int first = r;
        if (a[first] >= b[i]) continue;
        l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (c[mid] > b[i]) r = mid;
            else l = mid + 1;
        }
        int second = r;
        if (c[second] <= b[i]) continue;
        ans += (first + 1) * (n - second);
    }
    cout << ans << endl;
    return 0;
}

外卖店优先级

题目描述

"饱了么"外卖系统中维护着 N 家外卖店,编号 1 ∼ N。每家外卖店都有 一个优先级,初始时 (0 时刻) 优先级都为 0。

每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减 到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。

如果某家外卖店某时刻优先级大于 5,则会被系统加入优先缓存中;如果 优先级小于等于 3,则会被清除出优先缓存。

给定 T时刻以内的 M条订单信息,请你计算 T 时刻时有多少外卖店在优 先缓存中?
原题传送门

解题报告

和上面的日志那道题一样的思路,把每家店的订单分开,然后枚举时间,然后根据题目判断。

参考代码(C++版本)
#include <iostream>

using namespace std;

const int N = 2e5 + 10;
int cnt2[N];
pair<int, int>q[N];
int ans[N];
int cnt = 0;
int st[N];

int main() {
    int n, m, t;
    cin >> n >> m >> t;
    for (int i = 1; i <= m; i++) cin >> q[i].first >> q[i].second;
    for (int i = 1; i <= t; i++) {
        for (int j = 1; j <= m; j++) {
            if (i == q[j].first) {
                cnt2[q[j].second] += 2;
                st[q[j].second] = 1;
            }
        }
        for (int i = 1; i <= n; i++) {
            if (!st[i]) {
                cnt2[i]--;
                if (cnt2[i] < 0) cnt2[i] = 0;
            }
            else st[i] = 0;
            if (cnt2[i] > 5) {
                ans[i] = 1;
            }
            if (cnt2[i] <= 3 && ans[i] == 1) {
                ans[i] = 0;  
            }
        }
        //cout<<i<<endl;
    }
    for (int i = 1; i <= n; i++) {
        if (ans[i]) cnt++;
    }
    cout << cnt;
    return 0;
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习算法的小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值