C/C++程序设计竞赛真题实战特训教程(图解版)
上QQ阅读APP看书,第一时间看更新

第1章 枚举与模拟

1.1 卡片(★)

题目信息

2021 年省赛 - 填空题

C/C++ A组第1题;C/C++ B组第2题

Java B组第2题;Java C组第3题

Python 组第1题

【问题描述】

小蓝有很多数字卡片,每张卡片上都是一个数字(0到9)。

小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其他数了。

小蓝想知道自己能从1拼到多少。

例如,当小蓝有 30张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10,但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。

现在小蓝手里有 0 到 9 的卡片各 2021 张,共 20210 张,请问小蓝可以从 1拼到多少?

提示:建议使用计算机编程解决问题。

【答案提交】

这是一道结果填空题,你只需要算出结果后提交即可。本题的结果作为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

懒人速读

小蓝有很多数字卡片,每张卡片上都必然刻有 中的一个数字。

卡片可以用来拼凑数字,假设小蓝拥有刻有数字 和数字 的卡片各一张,那么他可以利用这两张卡片拼凑出数字 这4个正整数。

现给定刻有数字 的卡片各 张,小蓝将利用这些卡片从 1 开始连续拼凑数字,请问他最大能拼到数字几(注意:每张卡片只能利用一次)?

题目分析

核心考点

枚举

模拟

本题思路较为简单,可直接从数字 1 开始往后枚举,枚举的同时检查剩下的卡片能不能拼出当前枚举到的数字即可。

定义 表示刻有数字 的卡片张数。那么按照题目的意思,初始化 ,参考代码1-1-1如下。

参考代码1-1-1

 1   int cnt[10];
2   for(int i = 0 ; i <= 9 ; i ++) cnt[i] = 2021;

若我们将步骤拆分为两步,则可分为枚举、检查。枚举没什么问题,写个循环即可,但该怎么检查呢?

首先就需要把一个数在十进制下的每一位数字求出来。怎么求呢?可以将该数对 10 取模,这样就得到了个位上的数字;再除以 10,这样原本十位上的数字就跑到了个位上;然后再对 10 取模 ...... 反复这个过程,就可以得到这个数在十进制下所有数位上的数字了。

得到数位上的所有数字后再看看这些数字对应的卡片够不够,不够的话就说明这个数拼不了,就停止枚举,这样答案就为上一个枚举的数。

参考代码1-1-2

 1   bool check(int x){  // x 表示当前枚举的数
 2       while(x){
 3           int b = x % 10; // 获取十进制下的每个数位
 4           if(cnt[b] > 0) cnt[b] --; //  这个数位对应的卡片个数 - 1
 5           else return false; // 如果卡片不够了,则无法拼凑出该数
 6           x /= 10; // 将十位变为个位
 7       }
 8       return true;
9   }

至此,检查的模块就完成了。不过我们还可以进行一点小小的优化。

我们的枚举是从 1 开始的,如果分别看枚举数的个位、十位......上的数字,那么会得到如下的内容。

显然,个、十、百、千、万......每一数位的变化都是有周期性的。每个周期中各个数字出现的次数都是相同的,且每一数位都是从 1 开始变化的。因此,刻有数字 1的卡片一定会被最早使用完,我们只需要对该卡片的张数作判断就好,具体代码可见参考代码第 行。

参考代码1-1-3

  1   #include<bits/stdc++.h>
  2   using namespace std;
  3   int cnt[10];
  4   bool check(int x){  // x 表示当前枚举的数
  5       while(x){
  6           int b = x % 10; // 获取十进制下的每个数位
  7           if(b == 1) {
  8               if(cnt[1] == 0) return false;
  9               cnt[1] -- ;
 10           }
 11           x /= 10; // 将十位变为个位
 12       }
 13       return true;
 14   }
 15   signed main(){
 16       for(int i = 0 ; i <= 9 ; i ++) cnt[i] = 2021;
 17       for(int i = 1 ; ; i ++){
 18           if(!check(i)) return cout << i - 1 << '\n' , 0;
 19       }
 20       return 0;
21   }

最终答案为3181。