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。


贪心:

  1. 位数最少:尽可能多地使用 8(每数字 7 棍),只在余数不为 0 时,用 1~2 个数字调整前缀,位数最多为 \(k\) 或 \(k+1\)。可以证明没有更少位数的方案。
  2. 在同位数下字典序最小:调整的前缀都选用了能覆盖余数的、且数值最小的组合(如 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

posted @ 2026-10-01 18:48  Spencer20262026  阅读(15)  评论(0)    收藏  举报