蓝桥杯2022年第十三届决赛真题-卡牌

文章讨论了一道编程问题,小明试图用他的卡牌和空白牌凑出最多的完整卡牌套。给定每种卡牌的数量和可手写的限制,需要找出最大可能的套牌数。两种解法被提出:遍历循环和二分枚举,但前者可能导致超时。代码示例展示了这两种方法的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

这天,小明在整理他的卡牌。

他一共有 n 种卡牌,第 i 种卡牌上印有正整数数 i(i ∈ [1, n]),且第 i 种卡牌 现有 ai 张。

而如果有 n 张卡牌,其中每种卡牌各一张,那么这 n 张卡牌可以被称为一 套牌。小明为了凑出尽可能多套牌,拿出了 m 张空白牌,他可以在上面写上数 i,将其当做第 i 种牌来凑出套牌。然而小明觉得手写的牌不太美观,决定第 i 种牌最多手写 bi 张。

请问小明最多能凑出多少套牌?

输入格式

输入共 3 行,第一行为两个正整数 n, m。

第二行为 n 个正整数 a1, a2, ..., an。

第三行为 n 个正整数 b1, b2, ..., bn。

输出格式

一行,一个整数表示答案。

样例输入

复制

 4 5
 1 2 3 4
 5 5 5 5

样例输出

复制

 3

提示

这 5 张空白牌中,拿 2 张写 1,拿 1 张写 2,这样每种牌的牌数就变为了 3, 3, 3, 4,可以凑出 3 套牌,剩下 2 张空白牌不能再帮助小明凑出一套。

对于 30% 的数据,保证 n ≤ 2000 ;

对于 100% 的数据,保证 n ≤ 2 × 105 ; ai , bi ≤ 2n; m ≤ n2 。

题解

解法一 遍历循环

此方法不完全可行,只能通过百分之四十的数据,然后就会运行超时

伪代码

 1. 将ai排序,取出其中最小的数amin,这个数加到总套数中,ai-amin
 2. 对于手写牌
     1.找到所有ai==0
     2.判断 该ai已经手写牌<=bi && 总手写牌<=m
     3.ai==0 加一
 3. 返回1

代码

 #include<iostream>
 #include<algorithm>
 using namespace std;
 typedef struct {
     int a;
     int b;
 } BR;
 ​
 ​
 //a是前面的数 b是后面的数
 bool compare(BR br1, BR br2) { return br1.a < br2.a; }
 BR br1[200500];
 int main() {
     int min_brand;
     long long int n, m;
     cin >> n >> m;
     for (long long int i = 0; i < n; ++i) {
         cin >> br1[i].a;
     }
     for (long long int i = 0; i < n; ++i) {
         cin >> br1[i].b;
     }
     int flag = 0;
     long long int sum_brand = 0, sum = 0;
 ​
     while (1) {
         //找到所有数字中最小的数字
         sort(br1, br1 + n, compare);
         //每个数字都减去最小的数字
         int t; t = br1[0].a;
         sum_brand += t;
         for (int i = 0; i < n; ++i) {
             br1[i].a -= t;
         }
         for (int i = 0; i < n; ++i) {
             if (br1[i].a == 0) {
                 if (br1[i].b > 0 && sum < m) {
                     br1[i].b -= 1;
                     br1[i].a++;
                     sum++;
                 }
                 else {
                     flag = 1;
                     break;
                 }
             }
         }
         if (flag == 1) {
             break;
         }
     }
 ​
     cout << sum_brand;
     return 0;
 }

解法二 二分枚举

伪代码

 1. 找到二分查找上下界l r
 2. l < r进入循环
     2.1 mid = (r+l)/2
     2.2 考察每种牌是否符合条件
         2.2.1 条件1: A[i]+B[i] < mid
         2.2.2 条件2: mid-A[i] >= 手写牌余额
     2.3 根据是否符合条件更新r和l

代码

 #include<stdio.h>
 const int N = 2 * 1e5;
 ​
 int A[N], B[N];
 ​
 int main() {
     int n; long long m;
     scanf("%d %lld", &n, &m);
     for (int i = 0; i < n; ++i) scanf("%d", A + i);
     for (int i = 0; i < n; ++i) scanf("%d", B + i);
     int l = 0, r = 3 * n, mid;
     long long buf = m;
     while (l < r) {
         int flag = 1;
         mid = r + l + 1 >> 1;
         for (int i = 0; i < n; ++i) {
             if (A[i] >= mid) continue;
             if (A[i] + B[i] < mid || mid - A[i] >= buf) {
                 flag = 0;
                 break;
             }
             buf -= mid - A[i];
         }
         if (flag) l = mid; else r = mid - 1;
     }
     printf("%d", l);
     return 0;
 }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值