别忘了请点个赞+收藏+关注支持一下博主喵!!!
关注博主,更多蓝桥杯nice题目静待更新:)
枚举与模拟
一、卡片:
【问题描述】
小蓝有很多数字卡片,每张卡片上都是一个数字(0到9)。
小蓝准备用这些卡片来拼一些数,他想从1开始拼出正整数,每拼一个,就保存起来,卡片就 不能用来拼其他数。
想知道自己能从1拼到多少。
例如,当小蓝有30张卡片,其中0到9各3张,则小蓝可以拼出1到10,但是拼 11时卡片1已经只有一张了,不够拼出11。
现在小蓝手里有0到9的卡片各2021张,共20210张,请问小蓝可以从1拼到 多少? 提示:建议使用计算机编程解决问题。
【答案提交】
这是一道结果填空题,你只需要算出结果后提交即可。本题的结果作为一个整数,在 提交答案时只填写这个整数,填写多余的内容将无法得分
解析:
本题思路较为简单,可直接从数字1开始往后枚举,枚举的同时检查剩下的卡片能不能拼出当前枚举到的数字即可。
定义cnt[i] 表示刻有数字i的卡片张数。那么按照题目的意思,初始化cnt[0∼9]=2021, 参考代码如下。
int cnt[10];
for(int i = 0 ; i <= 9 ; i++) cnt[i] = 2021;
若我们将步骤拆分为两步,则可分为枚举、检查。枚举没什么问题,写个循环即可,但 该怎么检查呢?
首先就需要把一个数在十进制下的每一位数字求出来。怎么求呢? 可以将该数对 10 取 模,这样就得到了个位上的数字;再除以10,这样原本十位上的数字就跑到了个位上;然后 再对10取模……反复这个过程,就可以得到这个数在十进制下所有数位上的数字了。
得到数位上的所有数字后再看看这些数字对应的卡片够不够,不够的话就说明这个数拼 不了,就停止枚举,这样答案就为上一个枚举的数。
bool check(int x) { // x 表示当前枚举的数while (x) {int b = x % 10; // 获取十进制下的每个数位if (cnt[b] > 0) cnt[b]--; // 这个数位对应的卡片个数 -1else return false; // 如果卡片不够了,则无法拼凑出该数x /= 10; // 将十位变为个位}return true;
}
至此,检查的模块就完成了。不过我们还可以进行一点小小的优化。
我们的枚举是从1开始的,如果分别看枚举数的个位、十位……上的数字,那么会得到 如下的内容。
显然,个、十、百、千、万……每一数位的变化都是有周期性的。每个周期中各个数字出 现的次数都是相同的,且每一数位都是从1开始变化的。因此,刻有数字1的卡片一定会被 最早使用完,我们只需要对该卡片的张数作判断就好,具体代码可见参考代码第4∼14行。
#include <bits/stdc++.h>
using namespace std;int cnt[10];bool check(int x) { // x 表示当前枚举的数while (x) {int b = x % 10; // 获取十进制下的每个数位if (b == 1) {if (cnt[1] == 0) return false;cnt[1]--;}x /= 10; // 将十位变为个位}return true;
}signed main() {for (int i = 0; i <= 9; i++) cnt[i] = 2021;for (int i = 1;; i++) {if (!check(i)) return cout << i - 1 << '\n', 0;}return 0;
}
最终答案为3181。
二、回文日期:
【问题描述】
2020年春节期间,有一个特殊的日期引起了大家的注意:2020年2月2日。因为 如果将这个日期按“yyyymmdd”的格式写成一个8位数是20200202,恰好是一个回文数。我们称这样的日期是回文日期。
有人表示20200202是“千年一遇”的特殊日子。对此小明很不认同,因为不到2年 之后就是下一个回文日期:20211202即2021年12月2日。
也有人表示20200202并不仅仅是一个回文日期,还是一个ABABBABA型的回文 日期。对此小明也不认同,因为大约100年后就能遇到下一个ABABBABA型的回文日 期:21211212即2121年12月12日。算不上“千年一遇”,顶多算“千年两遇”。
给定一个8位数的日期,请你计算该日期之后下一个回文日期和下一个ABAB BABA型的回文日期各是哪一天。
【输入格式】
输入包含一个八位整数N,表示日期。 对于所有评测用例,10000101⩽N⩽89991231,保证N是一个合法日期的8位数表示。
【输出格式】
输出两行,每行1个八位数。第一行表示下一个回文日期,第二行表示下一个ABAB BABA型的回文日期。
【样例输入】
20200202
【样例输出】
20211202 21211212
【答案提交】
这是一道结果填空题,你只需要算出结果后提交即可。本题的结果作为一个整数,在 提交答案时只填写这个整数,填写多余的内容将无法得分。
解析:
根据题意,我们可以将题目拆解为如何判断回文和如何枚举日期两个部分来解决。
1. 如何判断回文
对于一个8位数的整型日期,可以通过除法和取余运算来获取它的每一位数字。比如:
若整数date 的值为20050511,它从高到低的每一数位上的数可以通过下列方法得到。
• data / 10000000 = 2;
• data / 1000000 % 10 = 0;
• data / 100000 % 10 = 0;
• data / 10000 % 10 = 5;
• data / 1000 % 10 = 0;
• data / 100 % 10 = 5;
• data / 10 % 10 = 1;
• data % 10 = 1。
对于本题,若用a[1]、a[2]、a[3]、a[4]、a[5]、a[6]、a[7]、a[8] 分别存储每一位,则一个 回文日期须满足:
一个ABABBABA 型回文日期须满足以下条件。
a[1] = a[3] = a[6] = a[8];a[2] = a[4] = a[5] = a[7].
对于一个日期字符串,可以直接通过下标来获取它的每一位数字。例如,string date = “20050511”,它从高到低的每一位分别可以通过data[0],data[1],...,data[7] 得到。判断回文日期及ABABBABA型回文日期的方式和上述方法类似。
不难看出,字符串获取日期的每一位数字是要比整型简单的,所以在判断回文日期及 ABABBABA 型回文日期时可以将整型转换为字符串来操作,如参考代码所示。
#include <bits/stdc++.h>
using namespace std;int date = 20050511;
string s = to_string(date); // 将整型转换为字符串, s = "20050511"// 判断回文日期
bool check1(int date) {string s = to_string(date);if (s[0] == s[7] && s[1] == s[6] && s[2] == s[5] && s[3] == s[4])return true;return false;
}// 判断 ABABBABA 型回文日期
bool check2(int date) {string s = to_string(date);if (s[0] == s[2] && s[0] == s[5] && s[0] == s[7] && s[1] == s[3] && s[1] == s[4] && s[1] == s[6])return true;return false;
}
2. 如何枚举日期
对于一个整型日期,可以用最简单的方式枚举,即令该日期不断+1,直到出现满足ABAB BABA 型的回文日期。
但这样存在一个问题:会枚举出许多不合法的日期(如20209999)。为此,我们需要对 日期进行合法性判断:设y,m,d分别表示年、月、日,month[i]表示第i个月的天数,那么当m12或dmonth[m]时日期不合法,反之日期合法,如参考代码所示。
#include <iostream>
#include <string>
using namespace std;int month[13] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};bool check(int date) {string s = to_string(date);//stoi->将字符串转为整型int y = stoi(s.substr(0, 4)), m = stoi(s.substr(4, 2)), d = stoi(s.substr(6, 2));if (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)) month[2] = 29;else month[2] = 28;if (m < 1 || m > 12 || d < 1 || d > month[m]) return false;return true;
}int main() {int date = 20050511;for (int i = date + 1;; i++) {if (!check(i)) continue;// 找到下一个有效日期后,可以在这里进行进一步处理// 例如,检查是否是回文日期或 ABABBABA 型回文日期cout << "Next valid date: " << i << endl;break;}return 0;
}
不过这样的枚举量是相当大的,要在比赛中完成本题要求的所有测试数据不太现实。
那么,还有什么枚举方法呢?
可以直接枚举合法日期。枚举合法日期只需模拟日期的变化规律即可,对应参考代码如 下所示。
#include <iostream>
using namespace std;int month[13] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};int main() {int date = 20050511;int y = date / 10000, m = date / 100 % 100, d = date % 100;for (int i = y;; i++) {if (i % 400 == 0 || (i % 100 != 0 && i % 4 == 0)) month[2] = 29; // 闰年2月有29天else month[2] = 28;int j = (i == y) ? m : 1; // 如果是i = y,则月份从m开始枚举,否则从1开始枚举for (; j <= 12; j++) {int k = (i == y && j == m) ? d : 1; // 如果i = y并且j = m,则日从d开始枚举,否则从1开始枚举for (; k <= month[j]; k++) {int new_date = i * 10000 + j * 100 + k;// 在这里可以进行进一步处理,例如检查是否是回文日期或ABABBABA型回文日期cout << "Next valid date: " << new_date << endl;return 0; // 找到第一个有效日期后退出程序}}}return 0;
}
综上 :
枚举合法日期:
#include <bits/stdc++.h>
using namespace std;int month[13] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int date, ans1, ans2;// 判断日期是否为回文
bool check1(int date) {string s = to_string(date);if (s[0] == s[7] && s[1] == s[6] && s[2] == s[5] && s[3] == s[4])return true;return false;
}// 判断日期是否满足 ABABBABA 型回文
bool check2(int date) {string s = to_string(date);if (s[0] == s[2] && s[0] == s[5] && s[0] == s[7] && s[1] == s[3] && s[1] == s[4] && s[1] == s[6])return true;return false;
}signed main() {cin >> date;int y = date / 10000, m = date / 100 % 100, d = date % 100;for (int i = y;; i++) {if (i % 400 == 0 || (i % 100 != 0 && i % 4 == 0))month[2] = 29;elsemonth[2] = 28;int j = (i == y) ? m : 1;for (; j <= 12; j++) {int k = (i == y && j == m) ? d + 1 : 1;for (; k <= month[j]; k++) {int new_date = i * 10000 + j * 100 + k;if (check1(new_date) && ans1 == 0)ans1 = new_date;if (check2(new_date)) {cout << ans1 << '\n' << new_date << '\n';return 0; // 当找到了 ABABBABA 型回文日期时,结束程序}}}}return 0;
}
枚举年份:
#include <bits/stdc++.h>
using namespace std;// month[1~12]分别记录1~12月每月的天数
int date, month[13] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};// 判断日期是否合法
string check(int date) {string s = to_string(date), t = to_string(date); // 将整型转换为字符串reverse(t.begin(), t.end()); // 翻转s += t; // 将s翻转后再与s拼接,拼接后的字符串一定会是个回文串int y = stoi(s.substr(0, 4)), m = stoi(s.substr(4, 2)), d = stoi(s.substr(6, 2));if (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)) month[2] = 29;else month[2] = 28;if (m < 1 || m > 12 || d < 1 || d > month[m]) return "-1";return s;
}// 判断日期是否是ABABBABA型回文
string check2(int date) {string s = to_string(date), t = to_string(date);reverse(t.begin(), t.end()); // 翻转s += t;if (s[0] == s[2] && s[0] == s[5] && s[0] == s[7] && s[1] == s[3] && s[1] == s[4] && s[1] == s[6]) return s;return "-1";
}signed main() {cin >> date;string ans1 = "";for (int i = date / 10000;; i++) {// 注意:date(输入的日期)不能作为答案if (check(i) == "-1" || check(i) == to_string(date)) continue;if (ans1 == "") ans1 = check(i);if (check2(i) != "-1") {cout << ans1 << '\n' << check2(i) << '\n';return 0;}}return 0;
}
以上就是这次博客的内容了
别忘了请点个赞+收藏+关注支持一下博主喵!!!
关注博主,更多蓝桥杯nice题目静待更新:)