题解:学而思编程 买整数

【题目来源】

学而思编程:买整数

【题目描述】

小猴去一个整数商店买一个整数。

整数商店出售范围在 \(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

【核心思想】

  1. 问题分析:给定参数 \(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\) 构成一个前缀区间,具有单调性。

  2. 算法选择

    • 整数二分:利用售价的单调性,在 \([0, 10^9]\) 上二分查找满足条件的最大 \(n\)
    • 位数计算:通过取模和整除操作计算 \(n\) 的十进制位数 \(d(n)\)
  3. 关键步骤

    • 判定函数 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\) 表示买不起任何整数)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(\log(10^9) \times \log_{10} n) = O(30 \times 10) = O(1)\),二分约 \(30\) 次,每次计算位数 \(O(\log_{10} n)\)
    • 空间复杂度:\(O(1)\),仅使用常数个变量
  5. 整数二分的核心思想

    • 单调性利用:售价函数 \(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
posted @ 2026-07-02 22:45  团爸讲算法  阅读(7)  评论(0)    收藏  举报