P11229 [CSP-J 2024] 小木棍题解
蒟蒻今天做这题时候觉得很有意思,一道j的第三题竟然能只用基础语句AC!?所以有感而发写了这篇题解,这里附上教师资格证(主要是怕有人不信这方法能过)

正文
题目概述
用恰好 \(n\) 根小木棍拼出一个没有前导零的正整数,要求拼出的数尽可能小。若无法拼出,输出 -1。
每个数字所需木棍数如下:
| 数字 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 木棍 | 6 | 2 | 5 | 5 | 4 | 5 | 6 | 3 | 7 | 6 |
多组测试数据,\(T \le 50\),\(n \le 10^5\)。
思路分析
要使拼出的数尽可能小,最重要的原则是位数尽可能少。在位数相同的前提下,再让高位数字尽可能小。
观察发现,数字 8 消耗的木棍最多(7 根),所以要想用尽量少的位数凑出 \(n\) 根木棍,应该尽可能多地使用数字 8。
设 \(k = \lfloor n / 7 \rfloor\),\(r = n \bmod 7\)。如果能用 \(k\) 个 8 直接凑出 \(n\)(即 \(r = 0\)),答案就是 \(k\) 个 8 组成的数。
若 \(r \neq 0\),则需要在前面添加一至两位数字来消耗掉多出的 \(r\) 根木棍,同时保证数值最小。因为 0 不能作为首位,且数字不能有前导零,所以不能简单地在前面加 0。
下面按余数 \(r\) 分类讨论,假设 \(k \ge 1\) 时我们可以将部分 8 替换为更省木棍的数字组合,使得总木棍数恰好为 \(n\),并且前缀尽可能小。
遍历
1. \(r = 0\)
全用 8,输出 \(k\) 个 8。
2. \(r = 1\)
若 \(n=1\),无解,输出 -1。
否则 \(n \ge 8\),此时 \(k \ge 1\)。
把最前面的一个 8(7 根)换成 10(2+6=8 根),刚好增加 1 根木棍,总数为 \(7(k-1)+8 = 7k+1 = n\)。
因为 10 是两位数,但这是最小的填法(不能是 01 或单独一个数字)。
输出:10 后接 \(k-1\) 个 8。
3. \(r = 2\)
在最前面放一个 1(2 根),后面接 \(k\) 个 8。
总木棍 \(2 + 7k = 7k+2 = n\),且没有前导零,最小。
输出:1 后接 \(k\) 个 8。
4. \(r = 3\)
情况稍复杂,需要按 \(k\) 的取值讨论:
- \(k = 0\)(即 \(n = 3\)):只能拼出
7(3 根)。 - \(k = 1\)(即 \(n = 10\)):若只用
1和8,余 2 根的做法会得到18(2+7=9,不够),28(5+7=12,多了)…… 最优是22(5+5=10)。 - \(k \ge 2\)(即 \(n \ge 17\)):用前缀
200(5+6+6=17)代替两个8(14 根),正好多出 3 根,总数 \(7(k-2)+17 = 7k+3 = n\)。
为什么不用1开头?若用1,需要再消耗 1 根变成某种组合,位数会增多,或高位更大。经过枚举,200是最小的。
输出:200后接 \(k-2\) 个8。
5. \(r = 4\)
- \(k = 0\)(\(n = 4\)):输出
4(4 根)。 - \(k \ge 1\)(\(n \ge 11\)):前缀
20(5+6=11)代替一个8(7 根),多出 4 根。
输出:20后接 \(k-1\) 个8。
6. \(r = 5\)
前缀 2(5 根),后面接 \(k\) 个 8。
\(n = 5\) 时,\(k=0\),直接输出 2。
输出:2 后接 \(k\) 个 8。
7. \(r = 6\)
前缀 6(6 根),后面接 \(k\) 个 8。
\(n = 6\) 时输出 6,\(k=0\) 同样成立。
输出:6 后接 \(k\) 个 8。
贪心:
- 位数最少:尽可能多地使用
8(每数字 7 棍),只在余数不为 0 时,用 1~2 个数字调整前缀,位数最多为 \(k\) 或 \(k+1\)。可以证明没有更少位数的方案。 - 在同位数下字典序最小:调整的前缀都选用了能覆盖余数的、且数值最小的组合(如
10、200、20等),后面的8已经是可选的最大数字,无法再改动。
代码实现
由于答案长度可能很大(\(n \le 10^5\) 时最多约 1.4 万个字符),直接输出字符
#include <bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
int k = n / 7; // 最多能放的 8 的个数
int r = n % 7;
if (n == 1) {
cout << -1 << endl;
continue;
}
if (r == 0) {
for (int i = 0; i < k; ++i) cout << 8;
cout << endl;
} else if (r == 1) {
cout << 10;
for (int i = 0; i < k - 1; ++i) cout << 8;
cout << endl;
} else if (r == 2) {
cout << 1;
for (int i = 0; i < k; ++i) cout << 8;
cout << endl;
} else if (r == 3) {
if (k == 0) {
cout << 7 << endl;
} else if (k == 1) {
cout << 22 << endl;
} else {
cout << 200;
for (int i = 0; i < k - 2; ++i) cout << 8;
cout << endl;
}
} else if (r == 4) {
if (k == 0) {
cout << 4 << endl;
} else {
cout << 20;
for (int i = 0; i < k - 1; ++i) cout << 8;
cout << endl;
}
} else if (r == 5) {
cout << 2;
for (int i = 0; i < k; ++i) cout << 8;
cout << endl;
} else if (r == 6) {
cout << 6;
for (int i = 0; i < k; ++i) cout << 8;
cout << endl;
}
}
return 0;
}
注意
这道题要记得换行哦(因为什么我不说,嘻嘻)
而且建议大家写上define int long long
浙公网安备 33010602011771号