J-L21 枚举与模拟
覆盖词条:KJ-30 | 先修:J-L20
模拟 · 循环与过程
小鱼的游泳时间
小鱼的游泳时间
题目描述
伦敦奥运会要到了,小鱼在拼命练习游泳。这一天,小鱼从 \(a\) 时 \(b\) 分一直游到当天的 \(c\) 时 \(d\) 分(24 小时制),请帮她算算这天一共游了多少时间。
输入格式
一行四个整数 \(a, b, c, d\),用空格隔开。
输出格式
一行两个整数 \(e\) 和 \(f\),用空格隔开,表示游了 \(e\) 小时 \(f\) 分钟(\(f < 60\))。
样例输入
样例输出
数据范围
- \(0 \le a, c \le 24\),\(0 \le b, d \le 60\),且结束时间一定晚于开始时间
小鱼的游泳时间 AC 代码
解题思路
全部换算成分钟再相减,避免借位:总分钟 \(= c \times 60 + d - (a \times 60 + b)\),小时为商、分钟为余。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int a, b, c, d;
cin >> a >> b >> c >> d;
int total = (c * 60 + d) - (a * 60 + b); // 全化成分钟再相减,避免借位
cout << total / 60 << " " << total % 60 << "\n";
return 0;
}
|
电子表
电子表
题目描述
电子表用 24 小时制显示时刻 hh:mm:ss(时、分、秒都用两位数字)。给出当前时刻和经过的秒数 \(n\),求 \(n\) 秒后电子表显示的时刻(超过一天则从 00:00:00 重新开始回绕)。
输入格式
第一行一个时刻 hh:mm:ss;第二行一个整数 \(n\)。
输出格式
一行,\(n\) 秒后的时刻,格式 hh:mm:ss(时、分、秒各两位,不足补 0)。
样例输入
样例输出
数据范围
- \(0 \le hh \le 23\),\(0 \le mm, ss \le 59\)
- \(0 \le n \le 10^9\)
⬇ 点击下载数据包
电子表 AC 代码
解题思路
过程模拟:把时刻全部换成秒,加上 \(n\) 后对一天的秒数 \(86400\) 取模回绕,再换回时分秒输出。与「小鱼的游泳时间」成对:那题是时刻差换算,这题是时刻推进。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int h, m, s;
long long n;
char ch; // ch 吃掉两个冒号
cin >> h >> ch >> m >> ch >> s >> n;
// 全部换成秒推进,再对一天的秒数取模回绕
long long total = (h * 3600LL + m * 60LL + s + n) % 86400;
cout << setw(2) << setfill('0') << total / 3600 << ":"
<< setw(2) << total / 60 % 60 << ":"
<< setw(2) << total % 60 << "\n";
return 0;
}
|
打印菱形
打印菱形
题目描述
输入一个奇数 \(n\),输出由 * 组成的菱形:共 \(n\) 行,最中间一行有 \(n\) 个 *,上下左右对称;第 1 行和第 \(n\) 行各 1 个 *。
输入格式
一行一个奇数 \(n\)。
输出格式
\(n\) 行菱形图案。
样例输入
样例输出
数据范围
- \(1 \le n \le 19\),且 \(n\) 为奇数
⬇ 点击下载数据包
打印菱形 AC 代码
解题思路
循环规律模拟:设 \(d = \min(i, n-1-i)\) 为该行到「菱形中轴」的距离,则第 \(i\) 行(从 0 起)有 \(n/2 - d\) 个前导空格、\(2d+1\) 个星号——把图形规律翻译成两个计数循环。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// 打印菱形:第 i 行(0 起)先输出 n/2-|n/2-i| 个空格,再输出 2*|n/2-i|+1 个 *
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int d = n / 2 - abs(n / 2 - i); // 该行星号个数为 2d+1
for (int s = 0; s < n / 2 - d; s++) cout << ' ';
for (int k = 0; k < 2 * d + 1; k++) cout << '*';
cout << "\n";
}
return 0;
}
|
金币
金币
题目描述
骑士第一天收到 1 枚金币;之后两天每天收到 2 枚;之后三天每天收到 3 枚……当连续 \(n\) 天每天收到 \(n\) 枚后,之后连续 \(n+1\) 天每天收到 \(n+1\) 枚。求前 \(k\) 天共获得多少金币。
输入格式
一个正整数 \(k\)。
输出格式
一个正整数,即骑士收到的金币数。
样例输入
样例输出
(1 + 2 + 2 + 3 + 3 + 3 = 14。)
数据范围
金币 AC 代码
解题思路
用 cur 枚举当前阶段(每天发 cur 枚、持续 cur 天),逐天累加,day < k 时截断最后一段。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int k;
cin >> k;
long long sum = 0;
int day = 0, cur = 0;
while (day < k) {
cur++; // 本阶段每天 cur 枚金币,共持续 cur 天
for (int i = 0; i < cur && day < k; i++) {
sum += cur;
day++;
}
}
cout << sum << "\n";
return 0;
}
|
统计天数
统计天数
题目描述
给出连续 \(N\) 天的最高气温,求最高气温一直上升的最长连续天数。
输入格式
第 1 行一个整数 \(N\);第 2 行 \(N\) 个整数,表示连续 \(N\) 天的最高气温。
输出格式
一行一个整数,表示最长连续上升天数。
样例输入
样例输出
(3 → 5 → 7 → 9。)
数据范围
- \(1 \le N \le 10^6\),最高气温 \(0 \sim 10^9\)
统计天数 AC 代码
解题思路
扫描数组,今天比昨天严格高则连续天数 +1,否则重置为 1,过程中取最大值。相等不算上升。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int best = 0, cur = 0, prev = INT_MIN;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
if (x > prev) cur++; // 严格上升才连续
else cur = 1;
best = max(best, cur);
prev = x;
}
cout << best << "\n";
return 0;
}
|
陶陶摘苹果
陶陶摘苹果
题目描述
苹果树上结了 10 个苹果,第 \(i\) 个到地面的高度为 \(h_i\)。陶陶把手伸直最大能够到的高度为 hand,够不到时可以踩一个 30 厘米高的板凳再试。假设碰到苹果苹果就会掉下来,求陶陶能摘到的苹果数目。
输入格式
第一行 10 个 100 到 200 之间的整数(苹果高度);第二行一个 100 到 120 之间的整数(伸直高度)。
输出格式
一行一个整数,能摘到的苹果数目。
样例输入
| 100 200 150 140 129 134 167 198 200 111
110
|
样例输出
数据范围
- 苹果高度 \(100 \sim 200\),伸直高度 \(100 \sim 120\)
陶陶摘苹果 AC 代码
解题思路
判定条件是 \(h_i \le hand + 30\)(踩上板凳再加 30 厘米),逐个比较计数。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int h[10];
for (int i = 0; i < 10; i++) cin >> h[i];
int hand;
cin >> hand;
int cnt = 0;
for (int i = 0; i < 10; i++)
if (h[i] <= hand + 30) cnt++; // 板凳高 30cm
cout << cnt << "\n";
return 0;
}
|
校门外的树
校门外的树
题目描述
长度为 \(l\) 的马路上,数轴 \(0 \sim l\) 每个整数点都种有一棵树。现在要移走 \(m\) 个区域(含端点)内的所有树,区域之间可能重叠。求移走后剩下多少棵树。
输入格式
第一行两个整数 \(l, m\);接下来 \(m\) 行每行两个整数 \(u, v\),表示区域的起止坐标。
输出格式
一行一个整数,表示剩余的树木数量。
样例输入
| 500 3
150 300
100 200
470 471
|
样例输出
数据范围
- \(1 \le l \le 10^4\),\(1 \le m \le 100\),\(0 \le u \le v \le l\)
校门外的树 AC 代码
解题思路
布尔数组逐点标记:每个区域内的树置为 false——标记法天然处理区域重叠,最后统计仍为 true 的点数。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int l, m;
cin >> l >> m;
vector<bool> tree(l + 1, true); // 数轴 0..l 每个点一棵树
while (m--) {
int u, v;
cin >> u >> v;
for (int i = u; i <= v; i++) tree[i] = false; // 标记法天然处理重叠区域
}
cout << count(tree.begin(), tree.end(), true) << "\n";
return 0;
}
|
质数筛
质数筛
题目描述
输入 \(n\) 个正整数,输出其中所有的质数(按输入顺序,用空格分隔)。
输入格式
第一行一个整数 \(n\);第二行 \(n\) 个正整数(不超过 1000)。
输出格式
一行,为所有质数,用空格隔开;没有质数则输出空行。
样例输入
样例输出
数据范围
- \(1 \le n \le 100\),正整数不超过 1000
质数筛 AC 代码
解题思路
试除法判质数:只要检查 \(2 \sim \sqrt{x}\) 之间的因子即可——若 \(x\) 有大于 \(\sqrt{x}\) 的因子,必有对应的小于 \(\sqrt{x}\) 的因子。
AC代码
| #include <bits/stdc++.h>
using namespace std;
bool isPrime(int x) {
if (x < 2) return false;
for (int i = 2; i * i <= x; i++)
if (x % i == 0) return false; // 试除只需到根号 x
return true;
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
if (isPrime(x)) cout << x << " ";
}
return 0;
}
|
枚举 · 数与数位
计数问题
计数问题
题目描述
试计算在区间 \(1\) 到 \(n\) 的所有整数中,数字 \(x\)(\(0 \le x \le 9\))共出现了多少次。例如在 1 到 11 中,数字 1 出现了 4 次。
输入格式
两个整数 \(n, x\),用空格隔开。
输出格式
一个整数,表示 \(x\) 出现的次数。
样例输入
样例输出
数据范围
- \(1 \le n \le 10^6\),\(0 \le x \le 9\)
计数问题 AC 代码
解题思路
从 1 到 \(n\) 逐个枚举,对每个数做数位拆分(v % 10 取末位、v /= 10 去末位),统计等于 \(x\) 的数位。从 1 开始枚举天然避免了前导零问题。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n, x;
cin >> n >> x;
long long cnt = 0;
for (int i = 1; i <= n; i++) {
int v = i;
while (v) { // 逐位拆分
if (v % 10 == x) cnt++;
v /= 10;
}
}
cout << cnt << "\n";
return 0;
}
|
数字统计
数字统计
题目描述
请统计某个给定范围 \([L, R]\) 的所有整数中,数字 2 出现的次数。比如范围 [2, 22] 中,数字 2 在 2、12、20、21 中各出现 1 次,在 22 中出现 2 次,共出现 6 次。
输入格式
两个正整数 \(L\) 和 \(R\),用空格隔开。
输出格式
数字 2 出现的次数。
样例输入
样例输出
数据范围
- \(1 \le L \le R \le 100000\)
数字统计 AC 代码
解题思路
与计数问题同型:枚举区间内每个数,逐位拆分统计 2 的个数。注意 22 这类数要按位各计一次。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int L, R;
cin >> L >> R;
int cnt = 0;
for (int i = L; i <= R; i++) {
int v = i;
while (v) {
if (v % 10 == 2) cnt++;
v /= 10;
}
}
cout << cnt << "\n";
return 0;
}
|
水仙花数
水仙花数
题目描述
三位数中,若各位数字的立方和恰好等于它本身(如 \(153 = 1^3+5^3+3^3\)),就称为水仙花数。输入区间端点 \(a, b\),按从小到大输出 \([a, b]\) 内的所有水仙花数,每行一个;若区间内没有水仙花数,输出 no。
输入格式
一行两个整数 \(a, b\)。
输出格式
若干行,每行一个水仙花数;无解输出一行 no。
样例输入
样例输出
数据范围
- \(100 \le a \le b \le 999\)
⬇ 点击下载数据包
水仙花数 AC 代码
解题思路
数位枚举:逐个枚举区间内的三位数,拆出百、十、个位验证立方和——「枚举 + 拆位验证」的标准型。与「数字统计」同族,额外练「无解输出 no」的处理。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
bool found = false;
for (int x = a; x <= b; x++) {
int d1 = x / 100, d2 = x / 10 % 10, d3 = x % 10;
if (d1 * d1 * d1 + d2 * d2 * d2 + d3 * d3 * d3 == x) {
cout << x << "\n";
found = true;
}
}
if (!found) cout << "no\n";
return 0;
}
|
数字反转
数字反转
题目描述
给定一个整数 \(N\),请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零。
输入格式
一个整数 \(N\)。
输出格式
一个整数,表示反转后的新数。
样例输入
样例输出
数据范围
数字反转 AC 代码
解题思路
逐位取余累乘:\(r = r \times 10 + n \% 10\)。负号利用 C++ 负数取余的「向零截断」特性自然带在最低位上,负数直接成立;反转产生的前导零会被十进制表示自动消掉。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int r = 0;
while (n) { // C++ 的 % 与 / 对负数是「向零截断」,
r = r * 10 + n % 10; // 负号会自然带在最低位上,负数直接成立
n /= 10;
}
cout << r << "\n";
return 0;
}
|
数字反转(升级版)
数字反转(升级版)
题目描述
与数字反转不同的是:这个数可以是整数、小数、分数、百分数。整数反转是将所有数位对调;小数反转是把整数部分的数反转,再将小数部分的数反转,不交换整数部分与小数部分;分数反转是把分母的数反转,再把分子的数反转;百分数只改变数字部分。反转后的数须满足常见形式(最高位不为零、小数末尾没有多余的 0、分数不约分)。数据保证没有负数。
输入格式
一个实数 \(s\)。
输出格式
一个实数,即 \(s\) 的反转数。
样例输入
样例输出
数据范围
- 整数不大于 20 位;小数整数/小数部分不大于 10 位;分数分子分母不大于 10 位;百分数分子不大于 19 位
数字反转(升级版) AC 代码
解题思路
按符号分类:.、/、% 定位后分段处理——整数段反转去前导 0;小数段反转后去末尾多余的 0(反转后变成前导 0);分数两段各自反转;百分数只反转数字部分。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// 反转一段数字串并去掉反转后产生的前导 0(整数规则:除非本身为 0)
string rev(string s) {
reverse(s.begin(), s.end());
int i = 0;
while (i + 1 < (int)s.size() && s[i] == '0') i++;
return s.substr(i);
}
int main() {
string s;
cin >> s;
if (s.find('.') != string::npos) { // 小数:整数、小数分别反转
string a = s.substr(0, s.find('.')), b = s.substr(s.find('.') + 1);
reverse(b.begin(), b.end());
int j = 0;
while (j + 1 < (int)b.size() && b[j] == '0') j++; // 小数末尾去掉多余的 0
b = b.substr(j);
cout << rev(a) << "." << b << "\n";
} else if (s.find('/') != string::npos) { // 分数:分子、分母各自反转,不约分
string a = s.substr(0, s.find('/')), b = s.substr(s.find('/') + 1);
cout << rev(a) << "/" << rev(b) << "\n";
} else if (s.back() == '%') { // 百分数:只反转数字部分
cout << rev(s.substr(0, s.size() - 1)) << "%\n";
} else {
cout << rev(s) << "\n";
}
return 0;
}
|
回文日期
回文日期
题目描述
一个日期用 8 位数字表示(前 4 位年份、接下来 2 位月份、最后 2 位日期)。一个日期是回文的,当且仅当表示它的 8 位数字是回文的。给定两个日期,求它们之间(含两端)有多少个真实存在的日期是回文的。
闰年:年份是 4 的倍数但不是 100 的倍数,或为 400 的倍数;闰年 2 月 29 天。
输入格式
两行,每行一个 8 位数字,分别表示起始、终止日期(保证真实存在且 date1 不晚于 date2)。
输出格式
一个整数,表示之间回文日期的个数。
样例输入
样例输出
(20111102 是回文日期。)
数据范围
- 年份为 4 位且首位不为 0;date1 ≤ date2
回文日期 AC 代码
解题思路
逐天枚举要判断三万多次合法性,更快的做法是枚举年份、反推月日:年份后 4 位倒过来就是月日,再检查月日合法且落在区间内。一次枚举同时完成「回文性」与「存在性」两件事。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// y 年 m 月的天数(含闰年判断)
int days(int y, int m) {
int d[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (m == 2 && ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)) return 29;
return d[m];
}
int main() {
long long d1, d2;
cin >> d1 >> d2;
int cnt = 0;
// 枚举年份,由年份反推回文的月日(比逐天枚举快得多)
for (int y = d1 / 10000; y <= d2 / 10000; y++) {
int m = y % 10 * 10 + y / 10 % 10; // 年的第 7、8 位倒过来
int dd = y / 100 % 10 * 10 + y / 1000; // 年的第 5、6 位倒过来
if (m < 1 || m > 12) continue;
if (dd < 1 || dd > days(y, m)) continue;
long long v = (long long)y * 10000 + m * 100 + dd;
if (v >= d1 && v <= d2) cnt++;
}
cout << cnt << "\n";
return 0;
}
|
最大公约数和最小公倍数问题
最大公约数和最小公倍数问题
题目描述
输入两个正整数 \(x_0, y_0\),求满足「\(P, Q\) 是正整数,且 \(P, Q\) 以 \(x_0\) 为最大公约数、以 \(y_0\) 为最小公倍数」的 \(P, Q\) 的个数(\(P, Q\) 有序,算不同的两组)。
输入格式
一行两个正整数 \(x_0, y_0\)。
输出格式
一行一个数,表示满足条件的 \(P, Q\) 的个数。
样例输入
样例输出
(四组:3,60;15,12;12,15;60,3。)
数据范围
- \(2 \le x_0, y_0 \le 10^5\)
最大公约数和最小公倍数问题 AC 代码
解题思路
\(P\) 必是 \(x_0\) 的倍数,且 \(P \times Q = x_0 \times y_0\)。枚举 \(P = x_0, 2x_0, \ldots\) 直到 \(P^2 > x_0 y_0\)(与 \(Q\) 对称只枚举一半),验证 \(P \mid x_0 y_0\) 且 \(\gcd(P, Q) = x_0\) 即计数。gcd 用试除法求(辗转相除在 J-L32 才学,这里自定义函数从较小数向下枚举即可)。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// 试除版 gcd:从较小的数向下枚举找最大公约数
//(辗转相除法在 J-L32 才学,这里用自定义函数 + 枚举即可完成)
long long gcd(long long a, long long b) {
for (long long i = min(a, b); i >= 1; i--)
if (a % i == 0 && b % i == 0) return i;
return 1;
}
int main() {
long long x0, y0;
cin >> x0 >> y0;
long long cnt = 0;
// P 必为 x0 的倍数,枚举 P 直到 P*P 超过 x0*y0(P、Q 对称,只枚举一半)
for (long long P = x0; P * P <= x0 * y0; P += x0) {
if ((x0 * y0) % P != 0) continue;
long long Q = x0 * y0 / P;
if (gcd(P, Q) == x0) {
cnt += (P * P == x0 * y0) ? 1 : 2; // P==Q 时只算一组
}
}
cout << cnt << "\n";
return 0;
}
|
完全数
完全数
题目描述
一个正整数如果恰好等于它所有真因数(除它自身以外的因数)之和,就称为完全数(如 \(6 = 1 + 2 + 3\))。输入 \(n\),按从小到大输出 \(1 \sim n\) 中的所有完全数,每行一个;若没有完全数,输出 no。
输入格式
一个整数 \(n\)。
输出格式
若干行,每行一个完全数;无解输出一行 no。
样例输入
样例输出
数据范围
⬇ 点击下载数据包
完全数 AC 代码
解题思路
因数试除枚举:对每个 \(i\) 枚举 \(1 \sim i/2\) 的因数求真因数和,等于自身即输出。与「质数筛」成对:判素只关心有没有因数,完全数要把因数加起来——同一个试除框架的两种用途。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// 真因数和:从 1 枚举到 i/2(教学版直接枚举,n ≤ 10^4 可过)
int main() {
int n;
cin >> n;
bool found = false;
for (int i = 2; i <= n; i++) {
int s = 1; // 1 是任何 i>1 的真因数
for (int d = 2; d <= i / 2; d++)
if (i % d == 0) s += d;
if (s == i) {
cout << i << "\n";
found = true;
}
}
if (!found) cout << "no\n";
return 0;
}
|
你的飞碟在这儿
你的飞碟在这儿
题目描述
彗星与 UFO 小组的匹配规则:把名字中每个字母转成数字(A=1、B=2、……、Z=26)并连乘,结果对 47 取模。若彗星名与小组名的计算结果相同,输出 GO,否则输出 STAY。
输入格式
两行,每行一个由大写字母组成的字符串(彗星名、小组名)。
输出格式
一行,GO 或 STAY。
样例输入
样例输出
数据范围
你的飞碟在这儿 AC 代码
解题思路
模拟映射:字母转数字 \(c - 'A' + 1\),连乘过程中边乘边取模防止溢出,最后比较两个余数。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// 每个字母 A~Z 转成 1~26,连乘后对 47 取模
int calc(const string& s) {
long long prod = 1;
for (char c : s) prod = prod * (c - 'A' + 1) % 47;
return (int)prod;
}
int main() {
string comet, group;
cin >> comet >> group;
cout << (calc(comet) == calc(group) ? "GO" : "STAY") << "\n";
return 0;
}
|
枚举 · 组合与子集
换零钱
换零钱
题目描述
把 \(n\) 元钱兑换成 1 元、2 元、5 元三种零钱,要求三种面额每种至少一张。两种方案不同,当且仅当某一种面额的张数不同。求共有多少种不同的兑换方案。
输入格式
一个整数 \(n\)。
输出格式
一个整数,为方案数。
样例输入
样例输出
(两种:1+2+2+5 和 1+1+1+2+5。)
数据范围
- \(8 \le n \le 100\)(\(8 = 1 + 2 + 5\) 是最小的可兑换金额)
⬇ 点击下载数据包
换零钱 AC 代码
解题思路
三重循环组合枚举:枚举 5 元张数 \(z\)、2 元张数 \(y\)、1 元张数 \(x\),逐组验证 \(x + 2y + 5z = n\)(循环下界取 1 保证每种至少一张)。与「勾股数」成对:一个枚举后计数,一个枚举后判定。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// 三重循环枚举:1 元 x 张、2 元 y 张、5 元 z 张,每种至少一张
int main() {
int n;
cin >> n;
int cnt = 0;
for (int z = 1; z * 5 <= n; z++)
for (int y = 1; y * 2 + z * 5 <= n; y++)
for (int x = 1; x + y * 2 + z * 5 <= n; x++)
if (x + y * 2 + z * 5 == n) cnt++;
cout << cnt << "\n";
return 0;
}
|
勾股数
勾股数
题目描述
输入 \(n\),输出所有满足 \(a^2 + b^2 = c^2\)(\(1 \le a < b < c \le n\))的整数三元组:按 \(a\) 升序,\(a\) 相同时按 \(b\) 升序,每行输出 a b c(空格分隔);若无解,输出 no。
输入格式
一个整数 \(n\)。
输出格式
若干行,每行三个整数 a b c;无解输出一行 no。
样例输入
样例输出
| 3 4 5
5 12 13
6 8 10
8 15 17
9 12 15
12 16 20
|
数据范围
⬇ 点击下载数据包
勾股数 AC 代码
解题思路
枚举 + 数学判定:双重循环枚举 \(a < b\),用「平方数表」判定 \(a^2+b^2\) 是否为完全平方数,是则 \(c\) 唯一确定——比三重循环少一层。与「换零钱」成对:同样是多重枚举的活儿,能用数学判定就省一层循环。
AC代码
| #include <bits/stdc++.h>
using namespace std;
bool sq[200005]; // sq[i]:i 是否为完全平方数
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) sq[i * i] = true;
bool found = false;
for (int a = 1; a <= n; a++)
for (int b = a + 1; a * a + b * b <= n * n; b++) {
int c2 = a * a + b * b;
if (sq[c2]) { // c = √(a²+b²),且自动满足 b < c
cout << a << " " << b << " " << (int)sqrt((double)c2) << "\n";
found = true;
}
}
if (!found) cout << "no\n";
return 0;
}
|
火柴棒等式
火柴棒等式
题目描述
给你 \(n\) 根火柴棍,可以拼出多少个形如 \(A+B=C\) 的等式?其中 \(A\)、\(B\)、\(C\) 是用火柴棍拼出的整数(非零则最高位不为 0),加号与等号各需 2 根火柴棍。若 \(A \ne B\),则 \(A+B=C\) 与 \(B+A=C\) 视为不同的等式。\(n\) 根火柴棍必须全部用上。
输入格式
一个整数 \(n\)。
输出格式
一个整数,能拼成的不同等式的数目。
样例输入
样例输出
(两个等式为 0+1=1 和 1+0=1。)
数据范围
火柴棒等式 AC 代码
解题思路
预处理 0~9 每个数字需要的火柴数,再对任意数做数位拆分求总火柴数。枚举 \(a, b \le 1000\)(24 根火柴时更大数字拼不出来),\(c = a + b\),判断三者火柴数之和加 4 是否等于 \(n\)。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int m[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6}; // 数字 0~9 各需的火柴数
int sticks(int x) {
if (x == 0) return 6;
int s = 0;
while (x) {
s += m[x % 10];
x /= 10;
}
return s;
}
int main() {
int n, cnt = 0;
cin >> n;
for (int a = 0; a <= 1000; a++)
for (int b = 0; b <= 1000; b++) {
int c = a + b;
if (sticks(a) + sticks(b) + sticks(c) + 4 == n) cnt++; // + 和 = 各 2 根
}
cout << cnt << "\n";
return 0;
}
|
三连击
三连击
题目描述
将 \(1, 2, \ldots, 9\) 共 9 个数分成 3 组,组成 3 个三位数,使这 3 个三位数构成 \(1 : 2 : 3\) 的比例。输出所有满足条件的三位数(每组一行,按第一个数升序)。
输入格式
无。
输出格式
若干行,每行 3 个三位数。
样例输入
样例输出
| 192 384 576
219 438 657
273 546 819
327 654 981
|
数据范围
三连击 AC 代码
解题思路
首数 \(x\) 的范围是 \(123 \sim 329\)(\(3x \le 987\) 且 \(x \ge 123\))。枚举 \(x\),用位标记(1 << d)判定三个数合起来恰好用完 1~9 每个数字一次且不含 0。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
for (int x = 123; x <= 329; x++) { // 首数最小 123、最大 329
int y = 2 * x, z = 3 * x;
int t[3] = {x, y, z};
int used = 0;
bool ok = true;
for (int i = 0; i < 3 && ok; i++) // 数位判定:1~9 恰好各出现一次
while (t[i]) {
int d = t[i] % 10;
if (d == 0 || (used >> d & 1)) { ok = false; break; }
used |= 1 << d;
t[i] /= 10;
}
if (ok) cout << x << " " << y << " " << z << "\n";
}
return 0;
}
|
选数
选数
题目描述
已知 \(n\) 个整数 \(x_1, x_2, \ldots, x_n\) 和一个整数 \(k\)(\(k < n\))。从中选出 \(k\) 个整数相加,分别可以得到一系列的和。求和为素数的组合共有多少种(每种组合只算一次)。
输入格式
第一行两个整数 \(n, k\);第二行 \(n\) 个整数 \(x_i\)。
输出格式
一个整数,表示种类数。
样例输入
样例输出
(3+4=7、5+6=11 是素数。)
数据范围
- \(1 \le n \le 20\),\(k < n\)
- \(1 \le x_i \le 5 \times 10^6\)
选数 AC 代码
解题思路
\(n \le 20\),直接二进制子集枚举:\(2^{20} = 10^6\) 个子集,恰好选 \(k\) 个(popcount 判定)时求和判素数。试除法判素数只需枚举到根号。
AC代码
| #include <bits/stdc++.h>
using namespace std;
bool isPrime(int x) {
if (x < 2) return false;
for (int i = 2; i * i <= x; i++)
if (x % i == 0) return false;
return true;
}
int main() {
int n, k;
cin >> n >> k;
vector<int> a(n);
for (auto& x : a) cin >> x;
int cnt = 0;
for (int mask = 0; mask < (1 << n); mask++) { // 二进制子集枚举(位运算 L08 已学)
if (__builtin_popcount(mask) != k) continue; // 恰好选 k 个数
int sum = 0;
for (int i = 0; i < n; i++)
if (mask >> i & 1) sum += a[i];
if (isPrime(sum)) cnt++;
}
cout << cnt << "\n";
return 0;
}
|
组合的输出
组合的输出
题目描述
从 \(1 \sim n\) 中任取 \(r\) 个数(\(r \le n\)),输出所有组合:每个组合占一行、元素按从小到大排列、每个数字占 3 个场宽,所有组合按字典序输出。
输入格式
一行两个自然数 \(n, r\)。
输出格式
所有的组合。
样例输入
样例输出
| 1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
|
数据范围
- \(1 \le r \le n\),\(n \le 20\)(保证输出组合数不太多)
组合的输出 AC 代码
解题思路
迭代组合枚举(不依赖递归):维护递增数组 \(a_1 < a_2 < \cdots < a_r\);从右往左找第一个还能增大的位置(\(a_i \ne n - (r - i)\)),将其加 1,其后各位依次取最小可能值;无法增大时枚举结束。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n, r;
cin >> n >> r;
vector<int> a(r + 1);
for (int i = 1; i <= r; i++) a[i] = i;
while (true) {
for (int i = 1; i <= r; i++) cout << setw(3) << a[i]; // 每个数字占 3 场宽
cout << "\n";
int i = r;
while (i >= 1 && a[i] == n - (r - i)) i--; // 从右往左找第一个还能增大的位置
if (i == 0) break; // 已是最后一个组合
a[i]++;
for (int j = i + 1; j <= r; j++) a[j] = a[j - 1] + 1;
}
return 0;
}
|
kkksc03 考前临时抱佛脚
kkksc03 考前临时抱佛脚
题目描述
期末要考 4 科,第 \(i\) 科习题集有 \(s_i\) 道题,完成第 \(j\) 题需要时间 \(t_j\)。左右两个大脑可以同时计算 2 道不同的题目(仅限同一科),必须一科一科复习。求完成复习的最短总时间。
输入格式
共 5 行:第 1 行四个正整数 \(s_1, s_2, s_3, s_4\);第 2~5 行分别为四科每道题所需时间。
输出格式
一行,为复习完毕的最短时间。
样例输入
样例输出
(四科各自把题目分给左右脑,代价取较慢一侧:第一科 5、第二科 max(3,7)=7、第三科 6、第四科 max(2,4)=4,相加得 22。)
数据范围
- \(1 \le s_1, s_2, s_3, s_4 \le 20\)
- 每题时间 \(1 \sim 60\)
kkksc03 考前临时抱佛脚 AC 代码
解题思路
一科内部:把每道题分给左脑或右脑(二进制子集枚举,位运算 L08 已学),两侧时间取较大者为该科代价;四科代价相加即答案。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// 一科的最短复习时间:枚举每道题放左脑还是右脑(二进制子集枚举)
int solve(vector<int>& t) {
int sum = 0, best = INT_MAX;
for (int x : t) sum += x;
for (int mask = 0; mask < (1 << (int)t.size()); mask++) {
int left = 0;
for (int i = 0; i < (int)t.size(); i++)
if (mask >> i & 1) left += t[i];
best = min(best, max(left, sum - left)); // 两侧同时算,取较慢的一侧
}
return best;
}
int main() {
int s[4];
cin >> s[0] >> s[1] >> s[2] >> s[3];
int ans = 0;
for (int i = 0; i < 4; i++) {
vector<int> t(s[i]);
for (auto& x : t) cin >> x;
ans += solve(t); // 四科独立,最短时间相加
}
cout << ans << "\n";
return 0;
}
|
买铅笔
买铅笔
题目描述
要买 \(n\) 支铅笔,商店有 3 种包装(包装内数量、价格可能不同),包装不可拆开、只买同一种包装,可以多买。求最少需要花多少钱。
输入格式
第一行一个正整数 \(n\);接下来三行,每行两个正整数:包装内铅笔数量、包装价格。
输出格式
一个整数,最少需要花费的钱。
样例输入
样例输出
数据范围
买铅笔 AC 代码
解题思路
枚举三种包装:需要 \(\lceil n / cnt \rceil\) 份(向上取整),总花费 = 份数 × 价格,取最小。向上取整写成 (n + cnt - 1) / cnt。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int best = INT_MAX;
for (int i = 0; i < 3; i++) {
int cnt, price;
cin >> cnt >> price;
int packs = (n + cnt - 1) / cnt; // 向上取整:必须买够 n 支(可以多买)
best = min(best, packs * price);
}
cout << best << "\n";
return 0;
}
|
模拟 · 棋盘与规则推演
扫雷游戏
扫雷游戏
题目描述
\(n\) 行 \(m\) 列的雷区中,* 表示地雷格、? 表示非地雷格。求每个非地雷格周围(上、下、左、右、左上、右上、左下、右下八个方向)的地雷格数,地雷格原样输出 *。
输入格式
第一行两个整数 \(n, m\);接下来 \(n\) 行每行 \(m\) 个字符描述雷区。
输出格式
\(n\) 行 \(m\) 个字符,非地雷格输出周围地雷数。
样例输入
样例输出
数据范围
扫雷游戏 AC 代码
解题思路
遍历每个格子:地雷格直接输出 *;非地雷格检查八方向相邻格(注意边界)数出地雷数。方向偏移用两层循环枚举 \(\{-1,0,1\} \times \{-1,0,1\}\)。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<string> g(n);
for (auto& s : g) cin >> s;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (g[i][j] == '*') { // 地雷格原样输出
cout << '*';
continue;
}
int cnt = 0;
for (int di = -1; di <= 1; di++) // 八方向计数
for (int dj = -1; dj <= 1; dj++) {
int x = i + di, y = j + dj;
if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == '*') cnt++;
}
cout << cnt;
}
cout << "\n";
}
return 0;
}
|
神奇的幻方
神奇的幻方
题目描述
按 Siamese 方法构造 \(N \times N\)(\(N\) 为奇数)的幻方:1 写在第一行中间;之后按四条规则依次填写每个数 \(K\)——(1) 若 \(K-1\) 在第一行但不在最后一列,则 \(K\) 填在最后一行、\(K-1\) 右一列;(2) 若 \(K-1\) 在最后一列但不在第一行,则 \(K\) 填在第一列、\(K-1\) 上一行;(3) 若 \(K-1\) 在第一行最后一列,则 \(K\) 填在 \(K-1\) 正下方;(4) 否则若 \(K-1\) 右上方未填数,则 \(K\) 填在右上方,否则填在正下方。
输入格式
一个正整数 \(N\)。
输出格式
共 \(N\) 行,每行 \(N\) 个整数,相邻两数用空格隔开。
样例输入
样例输出
数据范围
- \(1 \le N \le 39\) 且 \(N\) 为奇数
神奇的幻方 AC 代码
解题思路
纯规则模拟:当前点 \((x, y)\),四条规则按题面顺序 if-else if 实现,顺序不能乱;规则 4 的「右上方」用 \(g[x-1][y+1]\) 是否为 0 判断。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<vector<int>> g(n + 1, vector<int>(n + 1, 0));
int x = 1, y = (n + 1) / 2; // 1 写在第一行的中间
g[x][y] = 1;
for (int k = 2; k <= n * n; k++) {
if (x == 1 && y != n) { x = n; y++; } // 规则 1
else if (y == n && x != 1) { y = 1; x--; } // 规则 2
else if (x == 1 && y == n) { x++; } // 规则 3
else if (!g[x - 1][y + 1]) { x--; y++; } // 规则 4:右上为空
else { x++; } // 规则 4:否则正下方
g[x][y] = k;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cout << g[i][j] << " \n"[j == n];
return 0;
}
|
蛇形填数
蛇形填数
题目描述
把 \(1 \sim n^2\) 填入 \(n \times n\) 方阵:从左上角开始,沿顺时针螺旋方向依次填数(先向右,撞到边界或已填过的格子就顺时针转向)。输出填好的方阵,行内数字用一个空格分隔。
输入格式
一个整数 \(n\)。
输出格式
\(n\) 行,每行 \(n\) 个整数,为填好的方阵。
样例输入
样例输出
| 1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
|
数据范围
⬇ 点击下载数据包
蛇形填数 AC 代码
解题思路
方向数组模拟:记录当前方向(右、下、左、上顺时针轮换),每填一个数前试探下一格——越界或已填就转向。「方向数组 + 试探转向」是走格子模拟的通用套路。与「神奇的幻方」成对:同是规则填表,转向规则不同。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int a[25][25];
int main() {
int n;
cin >> n;
int x = 1, y = 0, d = 0; // 当前要填的位置:从 (1,1) 左侧进入,先向右
int dx[4] = {0, 1, 0, -1}; // 右、下、左、上(顺时针)
int dy[4] = {1, 0, -1, 0};
for (int v = 1; v <= n * n; v++) {
while (true) { // 撞到边界或已填格就转向
int nx = x + dx[d], ny = y + dy[d];
if (nx < 1 || nx > n || ny < 1 || ny > n || a[nx][ny]) d = (d + 1) % 4;
else break;
}
x += dx[d];
y += dy[d];
a[x][y] = v;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cout << a[i][j] << " \n"[j == n];
return 0;
}
|
乒乓球
乒乓球
题目描述
给出一场比赛每个球的胜负记录(W 华华得分、L 对手得分、E 记录结束),分别计算 11 分制和 21 分制下的比赛结果。一局比赛在某一达到目标分(11 或 21)且分差大于等于 2 时结束;一局结束后下一局立刻开始;记录末尾未打完的一局也要输出当前比分。两部分结果之间由一个空行分隔。
输入格式
若干行字符串,由大写 W、L 和 E 组成,E 表示记录结束。
输出格式
两部分比分,每行一局,格式 a:b。
样例输入
| WWWWWWWWWWWWWWWWWWWWWWLWE
|
样例输出
数据范围
乒乓球 AC 代码
解题思路
把记录读成一整串,扫到 E 停止。写一个 report(target) 函数按分制模拟:逐球累加,某方达到 target 且分差 ≥2 时输出一局并清零,结尾把未打完的一局也输出——对 11 和 21 各跑一遍。
AC代码
| #include <bits/stdc++.h>
using namespace std;
// 按 target 分制输出一段记录的比分
void report(const string& s, int target) {
int a = 0, b = 0;
for (char c : s) {
if (c == 'W') a++;
else if (c == 'L') b++;
if ((a >= target || b >= target) && abs(a - b) >= 2) { // 到分且分差≥2 才结束
cout << a << ":" << b << "\n";
a = b = 0;
}
}
cout << a << ":" << b << "\n"; // 未打完的一局也输出当前比分
}
int main() {
string all, line;
while (cin >> line)
for (char c : line) {
if (c == 'E') { // E 之后的内容全部忽略
report(all, 11);
cout << "\n";
report(all, 21);
return 0;
}
all += c;
}
report(all, 11);
cout << "\n";
report(all, 21);
return 0;
}
|
多项式输出
多项式输出
题目描述
给出一元 \(n\) 次多项式各项的次数和系数,按规定格式输出:自变量为 \(x\),按次数递减排列;只含系数不为 0 的项;首项正号不输出、负号以 - 开头;项与项之间用 + 或 - 连接;系数绝对值为 1 且次数高于 0 时不输出 1;指数大于 1 输出 x^b、等于 1 输出 x、等于 0 只输出系数。
输入格式
共 2 行:第一行一个整数 \(n\);第二行 \(n+1\) 个整数,第 \(i\) 个表示第 \(n-i+1\) 次项的系数。
输出格式
按题目所述格式输出多项式。
样例输入
样例输出
数据范围
- \(0 \le n \le 100\),系数在 \(-100 \sim 100\) 之间
多项式输出 AC 代码
解题思路
从最高次往常数项扫,0 系数跳过;每项处理「符号、绝对值、x、指数」四件事的边界:非首项先输出符号、绝对值为 1 且有 x 时不输出 1、指数 1 输出 x、指数 0 只输出系数。用 first 标记区分首项。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
bool first = true; // 首项正号不输出
for (int i = n; i >= 0; i--) {
int a;
cin >> a;
if (a == 0) continue; // 系数为 0 的项不输出
if (!first) cout << (a > 0 ? "+" : "-");
else if (a < 0) cout << "-";
int v = abs(a);
if (i == 0) cout << v; // 常数项只输出系数
else {
if (v != 1) cout << v; // 系数绝对值为 1 不输出 1
cout << "x";
if (i > 1) cout << "^" << i;
}
first = false;
}
return 0;
}
|
谁拿了最多奖学金
谁拿了最多奖学金
题目描述
奖学金共五种:院士奖学金 8000 元(期末平均成绩 >80 且发表论文 ≥1);五四奖学金 4000 元(平均成绩 >85 且班级评议 >80);成绩优秀奖 2000 元(平均成绩 >90);西部奖学金 1000 元(平均成绩 >85 且西部省份学生);班级贡献奖 850 元(班级评议 >80 且学生干部)。符合条件即可同时获得多项。求获得最多奖学金的学生姓名(并列取先出现者)、其奖金总数和全体奖学金总额。
输入格式
第一行一个整数 \(N\);接下来 \(N\) 行每行一位学生:姓名、期末平均成绩、班级评议成绩、是否学生干部(Y/N)、是否西部学生(Y/N)、发表论文数。
输出格式
共 3 行:最高奖学金学生的姓名、其奖金总数、全体奖学金总额。
样例输入
| 2
Amy 88 83 Y N 1
Bob 90 90 N Y 0
|
样例输出
(Amy 得 院士8000+五四4000+班级贡献850=12850;Bob 得 五四4000+西部1000=5000,总额 17850。)
数据范围
- \(1 \le N \le 100\),姓名长度 ≤20,成绩 0~100,论文 0~10
谁拿了最多奖学金 AC 代码
解题思路
结构体(或逐变量)读入,按五条规则累加每人的奖金;用「严格大于」记录最高者(并列时因用 > 比较、先出现者保留),同时累加总额。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
string bestName;
int best = 0, total = 0;
for (int i = 0; i < n; i++) {
string name;
int avg, cls, papers, money = 0;
char leader, west;
cin >> name >> avg >> cls >> leader >> west >> papers;
if (avg > 80 && papers >= 1) money += 8000; // 院士奖学金
if (avg > 85 && cls > 80) money += 4000; // 五四奖学金
if (avg > 90) money += 2000; // 成绩优秀奖
if (avg > 85 && west == 'Y') money += 1000; // 西部奖学金
if (cls > 80 && leader == 'Y') money += 850; // 班级贡献奖
total += money;
if (money > best) { best = money; bestName = name; } // 并列取先出现者
}
cout << bestName << "\n" << best << "\n" << total << "\n";
return 0;
}
|
两只塔姆沃斯牛
两只塔姆沃斯牛
题目描述
\(10 \times 10\) 的网格中,. 空地、* 障碍物、C 两头牛、F Farmer John。每分钟 C 和 F 同时行动:若前方(含边沿)无障碍则按当前方向前进一步,否则原地顺时针转 90°。两者初始都朝正北。某一分钟结束时若在同一格,追捕结束;若移动时穿过对方(未同格)不算相遇。求 John 需要多少分钟抓住牛,若永远无法相遇输出 0。
输入格式
共 10 行,每行 10 个字符表示地图。
输出格式
一个整数,表示所需分钟数;无法相遇输出 0。
样例输入
| ....*.....
...C......
..........
...F......
..........
..........
..........
..........
..........
..........
|
样例输出
(C 与 F 相向而行,第 3 分钟在 (0,3) 相遇。)
数据范围
- 地图固定 \(10 \times 10\),保证只有一个 F 和一个 C,且初始不在同一格
两只塔姆沃斯牛 AC 代码
解题思路
用(位置 + 方向)描述每个实体,按分钟同步推演:前方越界或为 * 就原地转向,否则前进。永不相遇的判定:状态总数为 \(100 \times 4 \times 100 \times 4 = 160000\),步数超过它必然回到某个状态(循环),直接输出 0。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
vector<string> g(10);
int fx, fy, cx, cy, fd = 0, cd = 0; // 方向:0 上、1 右、2 下、3 左
for (int i = 0; i < 10; i++) {
cin >> g[i];
for (int j = 0; j < 10; j++) {
if (g[i][j] == 'F') { fx = i; fy = j; }
if (g[i][j] == 'C') { cx = i; cy = j; }
}
}
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
auto step = [&](int& x, int& y, int& d) {
int nx = x + dx[d], ny = y + dy[d];
if (nx < 0 || nx >= 10 || ny < 0 || ny >= 10 || g[nx][ny] == '*')
d = (d + 1) % 4; // 前方(含边沿)是障碍:原地顺时针转 90°
else { x = nx; y = ny; }
};
// 状态总数 = 100 格 × 4 方向(John)× 100 格 × 4 方向(牛)= 160000,
// 超过这个步数还没相遇,就永远不会相遇
for (int t = 1; t <= 160000; t++) {
step(fx, fy, fd);
step(cx, cy, cd); // 两者同时移动
if (fx == cx && fy == cy) { // 分针末同格才算相遇(穿格不算)
cout << t << "\n";
return 0;
}
}
cout << 0 << "\n";
return 0;
}
|
机器翻译
机器翻译
题目描述
翻译软件的内存中有 \(M\) 个单元。依次翻译 \(N\) 个单词:若单词在内存中,直接翻译;否则查词典并把该单词放入内存——内存已满时先淘汰最早进入内存的单词。求整个过程需要查多少次词典。
输入格式
第一行两个正整数 \(M, N\);第二行 \(N\) 个非负整数(不超过 1000)表示单词。
输出格式
一个整数,为查词典的次数。
样例输入
样例输出
数据范围
- \(1 \le M \le 100\),\(1 \le N \le 1000\),单词大小不超过 1000
机器翻译 AC 代码
解题思路
先进先出队列模拟(L17 的队列):用布尔数组记录单词是否在内存,内存中没有则查词典次数 +1 并入队,队满时先出队淘汰队首。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int m, n;
cin >> m >> n;
vector<int> mem; // 手写先进先出队列(L17 栈与队列)
vector<bool> in(1001, false); // 单词是否在内存中
int lookups = 0;
for (int i = 0; i < n; i++) {
int w;
cin >> w;
if (in[w]) continue; // 内存中有:直接翻译
lookups++; // 内存中没有:查词典
if ((int)mem.size() == m) { // 内存满:淘汰最早进入的单词
in[mem.front()] = false;
mem.erase(mem.begin());
}
mem.push_back(w);
in[w] = true;
}
cout << lookups << "\n";
return 0;
}
|
模拟 · 字符串与编码
统计单词数
统计单词数
题目描述
给定一个单词和一个文章,统计该单词在文章中出现的次数和第一次出现的位置(首字母在文章中的位置,从 0 开始)。匹配不区分大小写,但要求整词完全匹配——只是某单词一部分不算。没出现则输出 -1。
输入格式
共 2 行:第一行为给定单词(只含字母,长度 ≤10);第二行为文章(只含字母和空格,长度 ≤10⁶)。
输出格式
一行:找到则输出「次数 首位置」,否则输出 -1。
样例输入
| To
To be or not to be is a question
|
样例输出
数据范围
统计单词数 AC 代码
解题思路
以空格为界切出文章的每个单词,切分时统一转小写后与给定单词比较——整词相等才计数,同时记录第一次出现的位置(用「当前起始字符下标」维护)。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
string w, art;
cin >> w;
cin.ignore(); // 吃掉第一行末尾的换行
getline(cin, art);
for (auto& c : w) c = tolower(c);
int cnt = 0, first = -1, pos = 0;
string cur;
for (int i = 0; i <= (int)art.size(); i++) {
if (i == (int)art.size() || art[i] == ' ') { // 单词边界
if (!cur.empty()) {
for (auto& c : cur) c = tolower(c);
if (cur == w) { // 整词完全匹配(大小写不敏感)
cnt++;
if (first == -1) first = pos;
}
}
cur.clear();
pos = i + 1;
} else {
cur += art[i];
}
}
if (cnt == 0) cout << -1 << "\n";
else cout << cnt << " " << first << "\n";
return 0;
}
|
斯诺登的密码
斯诺登的密码
题目描述
找出句子中所有用英文表示的数字(≤20),将每个数字平方后对 100 取模得到两位数,把这些两位数按任意顺序排成一行组成新数(开头为 0 则去掉),求所有排列中最小的数。若无数字则输出 0。
数字词包括正规词 zero ~ twenty,以及非正规词:a、both(按 2 计)、another(按 1)、first(1)、second(2)、third(3)。
输入格式
一个含有 6 个单词的句子(以 . 结束,字符数不超过 1000)。
输出格式
一个整数(密码);没有数字则输出 0。
样例输入
| Obama is a two five zero.
|
样例输出
(a→01、two→04、five→25、zero→00;拼接最小排列 00010425,去前导零为 10425。)
数据范围
斯诺登的密码 AC 代码
解题思路
用 map 建立单词到数字的映射(含非正规词);每个数字平方取模 100 后补成两位字符串;排序时用拼接比较(a+b < b+a)保证整体最小,拼接后去掉前导 0(保留最后一个 0)。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
// 正规数字词 + 非正规数字词的映射(题面规则 1)
map<string, int> num = {
{"zero", 0}, {"one", 1}, {"two", 2}, {"three", 3}, {"four", 4},
{"five", 5}, {"six", 6}, {"seven", 7}, {"eight", 8}, {"nine", 9},
{"ten", 10}, {"eleven", 11}, {"twelve", 12}, {"thirteen", 13},
{"fourteen", 14}, {"fifteen", 15}, {"sixteen", 16}, {"seventeen", 17},
{"eighteen", 18}, {"nineteen", 19}, {"twenty", 20},
{"a", 1}, {"both", 2}, {"another", 1}, {"first", 1}, {"second", 2}, {"third", 3}};
vector<string> pieces;
string w;
while (cin >> w) {
if (w == ".") break;
if (num.count(w)) {
int s = num[w] * num[w] % 100; // 平方后取后两位
string t = to_string(s);
if (s < 10) t = "0" + t; // 补成两位
pieces.push_back(t);
}
}
sort(pieces.begin(), pieces.end(),
[](const string& a, const string& b) { return a + b < b + a; }); // 拼接比较
string res;
for (auto& s : pieces) res += s;
int i = 0;
while (i + 1 < (int)res.size() && res[i] == '0') i++; // 去前导 0(末位保留)
res = res.substr(i);
cout << (res.empty() ? "0" : res) << "\n";
return 0;
}
|
字符串的展开
字符串的展开
题目描述
字符串中形如 d-h、4-8 的减号可按参数展开:仅当减号两侧同为小写字母或同为数字、且右边严格大于左边时展开。参数 \(p_1\) 决定填充方式(1 填小写/数字、2 字母填大写、3 填与个数相同的 *);参数 \(p_2\) 决定每个字符重复次数;参数 \(p_3\) 为 1 顺序、2 逆序。右边恰为左边后继时只删除减号;右边小于等于左边时保留减号。减号两侧字符不变。
输入格式
共两行:第一行三个正整数 \(p_1, p_2, p_3\);第二行为一行字符串(仅数字、小写字母和减号)。
输出格式
一行,为展开后的字符串。
样例输入
样例输出
| abcsttuuvvw1234556677889s-4ww
|
数据范围
- \(1 \le p_1 \le 3\),\(1 \le p_2 \le 8\),\(1 \le p_3 \le 2\)
- 字符串长度不超过 100
字符串的展开 AC 代码
解题思路
逐字符扫描:减号要做「是否可展开」五连判定(两侧同类型、右大于左);可展开则按 \(p_1/p_2\) 构造中间串、按 \(p_3\) 决定是否逆序;后继特判只删减号。其余字符原样输出。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int p1, p2, p3;
string s;
cin >> p1 >> p2 >> p3 >> s;
for (int i = 0; i < (int)s.size(); i++) {
char L = (i ? s[i - 1] : 0), R = (i + 1 < (int)s.size() ? s[i + 1] : 0);
bool expand = (s[i] == '-' && i && i + 1 < (int)s.size()
&& ((isalpha(L) && isalpha(R)) || (isdigit(L) && isdigit(R)))
&& R > L); // 展开前提:同类型且右严格大于左
if (!expand) { cout << s[i]; continue; }
if (R == L + 1) continue; // 后继:只删除减号
string mid;
for (char c = L + 1; c < R; c++) {
char b = c;
if (p1 == 2 && isalpha(c)) b = toupper(c); // p1=2 字母填大写
if (p1 == 3) b = '*'; // p1=3 填星号
for (int k = 0; k < p2; k++) mid += b;
}
if (p3 == 2) reverse(mid.begin(), mid.end()); // p3=2 逆序输出
cout << mid;
}
return 0;
}
|
小书童——凯撒密码
小书童——凯撒密码
题目描述
密码由原文字符串中每个字母向后移动 \(n\) 位形成,z 的下一个字母回绕到 a。给定原文字符串(不超过 50 个小写字母)和 \(n\),求密码。
输入格式
第一行一个整数 \(n\);第二行为原文字符串。
输出格式
一行,为加密后的密码。
样例输入
样例输出
数据范围
- 字符串长度 \(\le 50\),\(1 \le n \le 26\)
小书童——凯撒密码 AC 代码
解题思路
每个字母变为 (c - 'a' + n) % 26 + 'a'——先转到 0~25 的偏移量、加 n 后对 26 取模实现回绕,再转回字符。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
string s;
cin >> n >> s;
for (auto& c : s) c = (c - 'a' + n) % 26 + 'a'; // 向后移 n 位,z 回绕到 a
cout << s << "\n";
return 0;
}
|
手机
手机
题目描述
九键键盘上按出字母需要按数字键多次:a 按 2 键一下、b 两下、c 三下…… s 要按 7 键四下;空格按 0 键一下。给定只含小写字母和空格的句子,求打出这个句子至少需要按多少下键盘。
输入格式
一行句子,只包含小写字母和空格,不超过 200 个字符。
输出格式
一行一个整数,表示按键总次数。
样例输入
样例输出
(a 一下、b 两下、c 三下、d 一下,共 7 下。)
数据范围
手机 AC 代码
解题思路
打一张「每个字母按几下」的查表:{1,2,3, 1,2,3, 1,2,3, 1,2,3, 1,2,3, 1,2,3,4, 1,2,3, 1,2,3,4}(注意 s 和 z 是所在键的第 4 个字母);空格计 1 下,逐字符累加。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
string s;
getline(cin, s);
// 九键键盘:a b c 各 1/2/3 下 …… p q r s 各 1~4 下;空格按 0 键 1 下
int cnt[26] = {1, 2, 3,
1, 2, 3,
1, 2, 3,
1, 2, 3,
1, 2, 3,
1, 2, 3, 4,
1, 2, 3,
1, 2, 3, 4};
int total = 0;
for (char c : s) total += (c == ' ' ? 1 : cnt[c - 'a']);
cout << total << "\n";
return 0;
}
|
自动修正
自动修正
题目描述
已知一个英文字母(可能是大写或小写),输出它对应的大写形式。
输入格式
一个英文字符。
输出格式
对应的大写字符。
样例输入
样例输出
数据范围
自动修正 AC 代码
解题思路
一个 toupper 函数解决;也可以手写:若在 'a'~'z' 之间就减去 'a'-'A' 的差值。
AC代码
| #include <bits/stdc++.h>
using namespace std;
int main() {
char c;
cin >> c;
cout << (char)toupper(c) << "\n";
return 0;
}
|