1 条题解
-
0
你好!我是你的OI教练。这道题(B3868)是逻辑判断类的基础题,非常适合用来练习字符处理和布尔值(True/False)思维。
它不像前两题需要复杂的计算,而是需要像安检员一样,检查字符串里的每一个“乘客”(字符)是否符合规定。
1. 读题关键词:你的“雷达”响了吗?
- “分别判断”:
- 你需要做4次独立的检查:二进制、八进制、十进制、十六进制。
- 输出是 4 个
0或1,别漏了空格。
- “可能以 0 开头”:
- 这提示我们处理的是字符串,而不是整数。如果是整数,前导0会被忽略,但在这里
01是合法的。
- 这提示我们处理的是字符串,而不是整数。如果是整数,前导0会被忽略,但在这里
- “GG” -> “0 0 0 0”:
- 样例2给出了关键提示:如果出现了
G这样超出十六进制范围(0-9, A-F)的字符,那么它连十六进制都不是。 - 隐含条件:题目说字符串由“数字和大写字母”组成,这意味着可能出现
G-Z这种无效字符。
- 样例2给出了关键提示:如果出现了
2. 预备知识点
- 字符范围判断:
- 数字:
c >= '0' && c <= '9' - 字母:
c >= 'A' && c <= 'F'(十六进制专用)
- 数字:
- 布尔标志位 (Flag):
- 假设初始是合法的 (
true),一旦发现一个坏蛋,立刻变成不合法 (false) 并且可以停止检查。
- 假设初始是合法的 (
- 逻辑“与”关系:
- 一个数要是二进制,必须每一位都是 0 或 1。只要有一位不是,它就完了。
3. 启发式教学:草稿纸上的推演
来,我们画一张“安检过滤网”。假设字符串是 。
检查通道 (进制) 允许通过的字符名单 判定逻辑 举例: "15A" 举例: "101" 二进制 '0', '1'如果发现任何字符不在名单里 0,否则 1 '5'不在 0 全在 1 八进制 '0' ~ '7'如果发现字符 或 是字母 0 'A'不在 0 十进制 '0' ~ '9'如果发现字符是字母 0 十六进制 '0' ~ '9','A' ~ 'F'如果发现字符 (如 G, H...) 0 全在 1 推演结论: 你需要写四个独立的检查逻辑(或者一个通用的函数),遍历字符串的每一位。
- 草稿纸写法:
- 设
is_bin = true,is_oct = true,is_dec = true,is_hex = true。 - 读入字符 '1':都没事。
- 读入字符 '5':
is_bin变成false。其他没事。 - 读入字符 'A':
is_oct变成false,is_dec变成false。is_hex没事。 - 最后输出这四个变量。
- 设
4. 标准代码 (NOIP C++14)
/** * 题目:B3868 [GESP202309 三级] 进制判断 * 考点:字符串遍历、字符范围判断 * 难度:普及- / GESP三级 */ #include <iostream> #include <string> #include <vector> using namespace std; // 检查是否符合二进制 bool checkBin(string s) { for (char c : s) { // 二进制只能是 '0' 或 '1' if (c != '0' && c != '1') return false; } return true; } // 检查是否符合八进制 bool checkOct(string s) { for (char c : s) { // 八进制只能是 '0' - '7' if (c < '0' || c > '7') return false; } return true; } // 检查是否符合十进制 bool checkDec(string s) { for (char c : s) { // 十进制只能是 '0' - '9' if (c < '0' || c > '9') return false; } return true; } // 检查是否符合十六进制 bool checkHex(string s) { for (char c : s) { // 十六进制可以是 '0'-'9' 或者 'A'-'F' bool isDigit = (c >= '0' && c <= '9'); bool isHexChar = (c >= 'A' && c <= 'F'); // 如果既不是数字,也不是A-F,那就是非法的(比如 G, H, Z) if (!isDigit && !isHexChar) return false; } return true; } void solve() { string s; cin >> s; // 分别调用四个函数进行判断 // 注意:C++中 true 输出为 1,false 输出为 0,正好符合题目要求 cout << checkBin(s) << " " << checkOct(s) << " " << checkDec(s) << " " << checkHex(s) << endl; } int main() { // IO优化 ios::sync_with_stdio(false); cin.tie(0); int n; if (cin >> n) { while (n--) { solve(); } } return 0; }
5. 数据生成器 (1.in - 10.in)
为了全面测试,我们需要生成包含各种字符组合的数据,特别要注意题目提到的“数字和大写字母”范围,包括无效的
G-Z。/** * B3868 数据生成器 * 生成 1.in/1.out ~ 10.in/10.out */ #include <iostream> #include <fstream> #include <string> #include <vector> #include <cstdlib> #include <ctime> #include <algorithm> using namespace std; // ========================================== // Part 1: 标准题解逻辑 (用于生成 .out) // ========================================== bool isBin(string s) { for (char c : s) if (c != '0' && c != '1') return false; return true; } bool isOct(string s) { for (char c : s) if (c < '0' || c > '7') return false; return true; } bool isDec(string s) { for (char c : s) if (c < '0' || c > '9') return false; return true; } bool isHex(string s) { for (char c : s) { if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) return false; } return true; } // ========================================== // Part 2: 数据构造逻辑 // ========================================== int randInt(int min, int max) { return rand() % (max - min + 1) + min; } // 字符集定义 const string CHARS_BIN = "01"; const string CHARS_OCT = "01234567"; const string CHARS_DEC = "0123456789"; const string CHARS_HEX = "0123456789ABCDEF"; const string CHARS_ALL = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 含非法字符 G-Z string generateString(int type, int len) { string s = ""; string dictionary; // type决定了我们主要从哪个字典里取词,构造特定的测试点 if (type == 2) dictionary = CHARS_BIN; else if (type == 8) dictionary = CHARS_OCT; else if (type == 10) dictionary = CHARS_DEC; else if (type == 16) dictionary = CHARS_HEX; else dictionary = CHARS_ALL; // type = 0, 混合乱炖,可能包含 G-Z for (int i = 0; i < len; i++) { s += dictionary[randInt(0, dictionary.length() - 1)]; } return s; } void makeData(int fileId) { string inName = to_string(fileId) + ".in"; string outName = to_string(fileId) + ".out"; ofstream fin(inName); ofstream fout(outName); int N; if (fileId <= 2) N = 10; else if (fileId <= 5) N = 100; else N = 1000; fin << N << endl; for (int i = 0; i < N; i++) { int len = randInt(1, 10); // 题目保证长度不超过10 string s; // 精心设计测试点分布 if (fileId == 1) { // 基础:主要测试纯二进制和纯十进制 s = generateString(randInt(0, 1) ? 2 : 10, len); } else if (fileId == 3) { // 专测非法字符:强制混入 G-Z s = generateString(0, len); // 强制加个Z确保非法 s += "Z"; if(s.length() > 10) s.pop_back(); // 保持长度合法 } else if (fileId == 4) { // 专测十六进制 (A-F) s = generateString(16, len); } else { // 随机混合 int dice = randInt(1, 5); if (dice == 1) s = generateString(2, len); else if (dice == 2) s = generateString(8, len); else if (dice == 3) s = generateString(10, len); else if (dice == 4) s = generateString(16, len); else s = generateString(0, len); // 完全随机 } fin << s << endl; // 计算 Output fout << isBin(s) << " " << isOct(s) << " " << isDec(s) << " " << isHex(s) << endl; } fin.close(); fout.close(); cout << "Generated Case " << fileId << " (" << N << " lines)" << endl; } int main() { srand((unsigned)time(NULL)); for (int i = 1; i <= 10; i++) { makeData(i); } return 0; }教练总结
这道题虽然简单,但它是逻辑严密性的试金石。
- 包含关系:二进制一定符合八进制要求,八进制一定符合十进制要求(错!八进制是0-7,十进制是0-9,但十进制可能包含8和9,所以八进制数一定是合法的十进制数串,但反之不然)。
- 修正:如果看作字符串集合:BinStr OctStr DecStr HexStr。所以如果是二进制,后面三个一定都是 1。如果是十进制,后面一定是 1。这道题不需要利用这个性质,直接暴力判断最稳妥。
- 边界防御:一定要考虑到
G-Z这种题目描述中隐含的“捣乱分子”。
- “分别判断”:
- 1
信息
- ID
- 14210
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 1
- 标签
- 递交数
- 3
- 已通过
- 3
- 上传者