小木棍
在给定 $T$ 组测试数据的情况下,利用给定的火柴棍数量 $n$,拼出一个没有前导零且数值最小的正整数;若由于火柴棍数量限制无法拼出任何正整数,则输出 $-1$。
题库2分钟阅读
小 S 喜欢收集小木棍。在收集了 根长度相等的小木棍之后,他闲来无事,便用它们拼起了数字。用小木棍拼每种数字的方法如下图所示。
现在小 S 希望拼出一个正整数,满足如下条件:
- 拼出这个数恰好使用 根小木棍;
- 拼出的数没有前导 ;
- 在满足以上两个条件的前提下,这个数尽可能小。
小 S 想知道这个数是多少,可 很大,把木棍整理清楚就把小 S 折腾坏了,所以你需要帮他解决这个问题。如果不存在正整数满足以上条件,你需要输出 进行报告。
输入格式
本题有多组测试数据。
输入的第一行包含一个正整数 ,表示数据组数。
接下来包含 组数据,每组数据的格式如下:
一行包含一个整数 ,表示木棍数。
输出格式
对于每组数据:输出一行,如果存在满足题意的正整数,输出这个数;否则输出 。
样例
输入样例 1
5123618输出样例 1
-1176208样例 1 解释
- 对于第一组测试数据,不存在任何一个正整数可以使用恰好一根小木棍摆出,故输出 。
- 对于第四组测试数据,注意 并不是一个满足要求的方案。摆出 、 以及 都恰好需要 根小木棍,但它们不是摆出的数最小的方案。
- 对于第五组测试数据,摆出 需要 根小木棍。可以证明摆出任何一个小于 的正整数需要的小木棍数都不是 。注意尽管拼出 也需要 根小木棍,但因为这个数有前导零,因此并不是一个满足要求的方案。
数据范围
对于所有测试数据,保证:,。
测试点编号 特殊性质 无 ^ A ^ B ^ 无 ^ 特殊性质 A:保证 是 的倍数且 。
特殊性质 B:保证存在整数 使得 ,且 。
题目要求用恰好 根木棍拼出一个没有前导零的最小正整数。
我们先列出拼出数字 分别需要的木棍数量:
0: 6根,1: 2根,2: 5根,3: 5根,4: 4根5: 5根,6: 6根,7: 3根,8: 7根,9: 6根
要让拼出的数字尽可能小,我们有两个最高优先级的贪心策略:
- 位数越少,数字越小:因此我们要让每位数字消耗的木棍尽可能多。消耗木棍最多的数字是
8(需要 7 根)。所以,我们要尽可能多地使用数字8。 - 高位数字越小,数字越小:在位数相同的情况下,最高位(最左边)的数字越小,整个数就越小。
基于“尽可能多用 8”的原则,我们可以把 对 取模(即 ),根据余数来决定最高位或前几位的数字组合。
一个数字如果完全由 8 组成,木棍数就是 的倍数。当 不是 的倍数时,余数会有 共 6 种情况。我们可以对余数进行分类讨论。
对于较大的 (例如 ),后面可以全部填 8,我们只需要搞定最前面的几位数:
- 余 0:最完美,全部填
8。 - 余 1:拿出 1 根,并从后面借 1 个
8(7根),凑成 8 根木棍,拼出最小两位数10。 - 余 2:多出 2 根,直接在最高位放
1。 - 余 3:拿出 3 根,并从后面借 2 个
8(14根),凑成 17 根木棍,拼出最小三位数200。 - 余 4:拿出 4 根,并从后面借 1 个
8(7根),凑成 11 根木棍,拼出最小两位数20。 - 余 5:多出 5 根,直接在最高位放
2。 - 余 6:多出 6 根,直接在最高位放
6(因为不能有前导零,所以不用0)。
对于 的较小情况,由于木棍太少,无法通过“借一个8”的方式自由组合,我们需要直接硬性特判(打表):
- : 没有任何数字只需 1 根木棍 输出
-1 - (注意样例解释:摆出
0不合法,摆出9、41、111都不是最小,最小是6)
#include <iostream>#include <string>
void solve() { int n; std::cin >> n;
// 对 n <= 13 的较小情况进行打表特判 if (n <= 13) { switch (n) { case 1: std::cout << -1 << "\n"; break; case 2: std::cout << 1 << "\n"; break; case 3: std::cout << 7 << "\n"; break; case 4: std::cout << 4 << "\n"; break; case 5: std::cout << 2 << "\n"; break; case 6: std::cout << 6 << "\n"; break; case 7: std::cout << 8 << "\n"; break; case 8: std::cout << 10 << "\n"; break; case 9: std::cout << 18 << "\n"; break; case 10: std::cout << 22 << "\n"; break; case 11: std::cout << 20 << "\n"; break; case 12: std::cout << 28 << "\n"; break; case 13: std::cout << 68 << "\n"; break; } return; }
int k = n / 7; int remainder = n % 7;
switch (remainder) { case 0: std::cout << std::string(k, '8') << "\n"; break; case 1: std::cout << "10" << std::string(k - 1, '8') << "\n"; break; case 2: std::cout << "1" << std::string(k, '8') << "\n"; break; case 3: std::cout << "200" << std::string(k - 2, '8') << "\n"; break; case 4: std::cout << "20" << std::string(k - 1, '8') << "\n"; break; case 5: std::cout << "2" << std::string(k, '8') << "\n"; break; case 6: std::cout << "6" << std::string(k, '8') << "\n"; break; }}
int main() { std::ios_base::sync_with_stdio(false); std::cin.tie(nullptr);
int t; if (std::cin >> t) { while (t--) { solve(); } } return 0;}