P11229 [CSP-J 2024] 小木棍

· · 题解

前言

我们班很多人这道题都爆了,原因是写分讨写挂了。不像睿智的我,直接暴力加上一点点思维。

解题思路

step 1

首先我们把每个数的贡献都列出来。

g(x) 为拼成 x 需要的木棍数量。

第一行表示数 x_i,第二行表示 g(x_i)

0 1 2 3 4 5 6 7 8 9
6 2 5 5 4 5 6 3 7 6

然后按照贡献分类。

由于表格不好列,就不列表格了。

每一行开头的数字代表 f,后面的是满足 g(x_{ij}) = f 的所有 x_{ij}

$3 : 7$。 $4 : 4$。 $5 : 2, 3, 5$。 $6 : 0, 6, 9$。 $7 : 8$。 我们可以观察到一些数字是没有用的,比如数字 $3,5$ 永远不是最优的,因为 $2 < 3 < 5$,且 $g(2) = g(3) = g(5)$,那么 $2$ 肯定是这三个数中最优的。 于是我们就可以精简成: $2 : 1$。 $3 : 7$。 $4 : 4$。 $5 : 2$。 $6 : 0, 6$。 $7 : 8$。 需要注意的是 $6$ 并不能直接删去,因为题目要求**没有前导 $0$**。比如 $n = 13$,答案应为是 $68$ 而不是 $08$ 或 $80$。 #### step 2 这时,我们再来考虑如何使答案尽可能小。 首先我们要满足答案尽可能小,这意味着答案的位数要尽可能小。比如 $1 < 10$。所以然后我们发现 $g(8)$ 是最大的,所以大多数情况下,答案肯定包含一堆 $8$。 为什么说是大多数情况下呢,比如 $n \le 12$ 时,只有当 $n = 8$ 或 $9$ 或 $12$ 的时候答案才包含 $8$。 #### step 3 然后我们继续考虑如何使答案尽可能小。 这次我们考虑的是如何使答案在位数一样的情况下选出最小值。 很明显 $8$ 一定只能跟在答案的最后面。 显然,会出现一些特殊情况导致 $x$ 个较小的数比 $x - a$ 个较小的数再加上 $a$ 个 $8$ 更优。于是我们只需要写一个暴力程序找找关系,发现对于任意答案只有至多前 $3$ 位不为 $8$,所以我们可以暴力枚举前 $3$ 位怎么组合最优。 需要注意的是由于不能有前导零,所以我们需要特殊处理,具体实现请看代码。 #### 注意 题目要求拼出一个**正整数**,所以答案不能为 $0$。 --- 谔谔,由于是考场上写的,为了保险,我枚举的是前 $5$ 位。而且建议不要使用 `to_string()`,因为本人亲测会超时。 CODE: ```cpp #include <bits/stdc++.h> using namespace std; int k[100], b[100010]; int main() { ios::sync_with_stdio(false); ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin >> t; k[0] = k[6] = k[9] = 6, k[1] = 2, k[2] = k[3] = k[5] = 5, k[4] = 4, k[8] = 7; while (t--) { int id2 = 0; int n; cin >> n; if (n == 1) { cout << "-1" << endl; } else if (n == 2) { cout << "1" << endl; } else if (n == 3) { cout << "7" << endl; } else if (n == 4) { cout << "4" << endl; } else if (n == 5) { cout << "2" << endl; } else if (n == 6) { cout << "6" << endl; } else if (n == 7) { cout << "8" << endl; } else if (n % 7 == 0) { for (int i = 1; i <= n / 7; i++) { cout << 8; } cout << endl; } else { while (n - 7 > 28) { b[++id2] = 8; n -= 7; } string a = "9999999999999999999"; for (int i = -1; i <= 9; i++) { for (int j = -1; j <= 9; j++) { for (int o = -1; o <= 9; o++) { for (int l = -1; l <= 9; l++) { for (int m = -1; m <= 9; m++) { //从 -1 开始是因为位数前面位数不一定为 5,也就是说其实是至多 5 位。 string b = ""; int sum = 0; if (i != -1) { b += i + '0'; sum += k[i]; } if (j != -1) { b += j + '0'; sum += k[j]; } if (o != -1) { b += o + '0'; sum += k[o]; } if (l != -1) { b += l + '0'; sum += k[l]; } if (m != -1) { b += m + '0'; sum += k[m]; } if (sum == n && b.size() > 0 && b[0] != '0') { if (a.size() > b.size()) a = b; else if (a.size() == b.size()) { a = min(a, b); } } } } } } } for (int i = 0; i < a.size(); i++) { b[++id2] = a[i] - '0'; } sort(b + 1, b + 1 + id2); int kk = 0; for (int i = 1; i <= id2; i++) { //找到第一个不为 0 的数,然后将其放到第一个位置输出。 if (b[i] != 0) { kk = i; cout << b[i]; break; } } for (int i = 1; i <= id2; i++) { if (i != kk) { cout << b[i]; } } cout << endl; } } return 0; } ```