题解:学而思编程 买整数
【题目来源】
学而思编程:买整数
【题目描述】
小猴去一个整数商店买一个整数。
整数商店出售范围在 \(1 \sim 10^9\) 的所有整数,整数 \(n\) 的售价是 \(a \times n+b \times d(n)\) 元,其中 \(d(n)\) 是 \(n\) 的十进制表示的位数。
例如 \(a=10,b=7\) 时,以下 \(3\) 个整数的价格是:
\(10\):\(10 \times 10 + 7 \times 2 = 114\)
\(100\):\(10 \times 100 + 7 \times 3 = 102\)
\(12345\):\(10 \times 12345 + 7 \times 5 = 123485\)
小猴只带了 \(x\) 元钱,求出他能买到的最大整数,如果一个整数都买不起,输出 \(0\)。
【输入】
\(1\) 行,\(3\) 个正整数 \(a,b,x\)。
【输出】
\(1\) 个整数,输出小猴能买到的最大整数,如果一个整数都买不起,输出 \(0\)。
【输入样例】
10 7 100
【输出样例】
9
【核心思想】
-
问题分析:给定参数 \(a, b\) 和预算 \(x\),整数 \(n\) 的售价为 \(a \times n + b \times d(n)\),其中 \(d(n)\) 是 \(n\) 的十进制位数。需要在 \([1, 10^9]\) 范围内找到售价不超过 \(x\) 的最大整数 \(n\)。关键观察是:售价随 \(n\) 增大而单调不减(\(a \times n\) 严格递增,\(b \times d(n)\) 阶梯递增),因此满足条件的 \(n\) 构成一个前缀区间,具有单调性。
-
算法选择:
- 整数二分:利用售价的单调性,在 \([0, 10^9]\) 上二分查找满足条件的最大 \(n\)
- 位数计算:通过取模和整除操作计算 \(n\) 的十进制位数 \(d(n)\)
-
关键步骤:
- 判定函数
check(n):- 计算 \(d(n)\):通过不断 \(n \leftarrow \lfloor n / 10 \rfloor\) 直至 \(n = 0\),统计除法次数
- 计算售价:\(price = a \times n + b \times d(n)\)
- 返回 \(price \leq x\)
- 二分查找:
- 范围 \([0, 10^9]\),取上中点 \(mid = \lfloor \frac{l + r + 1}{2} \rfloor\)
- 若
check(mid)为真:\(l = mid\)(尝试更大的 \(n\)) - 否则:\(r = mid - 1\)(缩小范围)
- 输出答案:\(l\)(最大满足条件的整数,若 \(l = 0\) 表示买不起任何整数)
- 判定函数
-
时间/空间复杂度:
- 时间复杂度:\(O(\log(10^9) \times \log_{10} n) = O(30 \times 10) = O(1)\),二分约 \(30\) 次,每次计算位数 \(O(\log_{10} n)\)
- 空间复杂度:\(O(1)\),仅使用常数个变量
-
整数二分的核心思想:
- 单调性利用:售价函数 \(f(n) = a \times n + b \times d(n)\) 随 \(n\) 单调不减,因此"售价 \(\leq x\)"的集合是 \([0, n_{max}]\) 形式的前缀,适合二分查找右边界
- 上中点取整:使用 \((l + r + 1) / 2\) 取上中点,配合 \(l = mid\) 和 \(r = mid - 1\) 的更新方式,确保二分能够收敛到最大满足值而非最小满足值
- 位数计算的简洁性:通过反复除以 \(10\) 统计位数,避免了将数字转为字符串的开销,同时适用于高精度场景
- 边界处理:左边界设为 \(0\) 而非 \(1\),统一处理"买不起任何整数"的情况(输出 \(0\)),无需额外特判
- 适用于单调性条件下的最优值搜索、预算约束下的最大化选择等场景
【解题思路】

【算法标签】
整数二分
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
int a, b, k;
// 检查数字x是否满足条件
bool check(int x)
{
int t = x; // 保存原始数字
int len = 0; // 计算数字x的位数
while (x)
{
len++;
x /= 10;
}
// 检查 a*x + b*位数 是否不超过k
return (a * t + b * len) <= k;
}
signed main()
{
cin >> a >> b >> k;
int l = 0, r = 1e9; // 二分查找范围
while (l < r)
{
int mid = (l + r + 1) / 2;
if (check(mid))
{
l = mid; // 满足条件,尝试更大的数
}
else
{
r = mid - 1; // 不满足条件,减小范围
}
}
cout << l << endl;
return 0;
}
【运行结果】
10 7 100
9
浙公网安备 33010602011771号