题解:洛谷 P2005 AB Problem II

【题目来源】

洛谷:P2005 A/B Problem II - 洛谷

【题目描述】

给出正整数 \(N\)\(M\),请你计算 \(\lfloor\frac{N}{M}\rfloor\)\(\frac{N}{M}\) 的下取整)。

【输入】

两行,两个正整数,\(N\)\(M\)

【输出】

一行,一个整数,表示 \(\lfloor\frac{N}{M}\rfloor\)

【输入样例】

1000
333

【输出样例】

3

【核心思想】

  1. 问题分析:给定两个正整数 \(N\)\(M\),可能非常大(远超基本数据类型),需要计算 \(\lfloor N/M \rfloor\) 的整数部分。传统的高精度除以低精度算法(逐位竖式除法)仅适用于除数 \(M\) 为普通整数的情况;若 \(M\) 本身也是高精度数,则需采用更通用的方法。本题通过二分答案 + 高精度乘法与比较,将除法转化为在单调序列中寻找最大 \(mid\),使得 \(M \times mid \le N\),从而得到商。

  2. 算法选择

    • 二分答案:商的取值范围为 \([0, 10^{18}]\)(或根据输入数据范围确定),具有单调性,即若 \(M \times mid \le N\),则所有小于 \(mid\) 的值也满足。因此可以二分枚举 \(mid\)
    • 高精度乘法:将 \(M\) 存储为高精度数,乘以整数 \(mid\),得到 \(M \times mid\) 的高精度结果。
    • 高精度比较:将乘积 \(M \times mid\) 与被除数 \(N\) 进行逐位比较,判断是否满足 \(\le N\)
    • 复杂度:二分次数约为 \(\log_2(10^{18}) \approx 60\) 次,每次进行高精度乘法(\(O(L)\))和比较(\(O(L)\)),总复杂度 \(O(L \log C)\),其中 \(L\)\(N\) 的位数,\(C\) 为商的上界。
  3. 关键步骤

    • 读入并存储:将 \(N\)\(M\) 作为字符串读入,转换为高精度数组(低位在前,方便乘法和比较)。
    • 二分查找:初始化左边界 \(l=0\),右边界 \(r=10^{18}\)(或根据数据范围调整,确保 \(r\) 足够大)。当 \(l < r\) 时,取 \(mid = \lceil (l+r)/2 \rceil\)(即 (l+r+1)//2),以避免死循环。
    • 检查函数:计算 \(C = M \times mid\)(高精度乘法),然后比较 \(C\)\(N\)
      • \(C\) 的位数小于 \(N\),或位数相同但 \(C\) 的每一位均小于等于 \(N\) 的对应位,则条件成立,令 \(l = mid\)
      • 否则,令 \(r = mid-1\)
    • 输出结果:循环结束后,\(l\) 即为满足 \(M \times l \le N\) 的最大整数,即 \(\lfloor N/M \rfloor\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(L \log C)\),其中 \(L\)\(N\) 的位数(高精度乘法与比较的代价),\(C\) 是二分搜索范围(通常为 \(10^{18}\),约 60 次迭代)。
    • 空间复杂度:\(O(L)\),用于存储高精度数和中间结果。
  5. 二分 + 高精度乘法的核心思想

    • 单调性转化:求商等于求满足 \(M \times x \le N\) 的最大整数 \(x\),该性质使得二分法适用,避免直接进行复杂的高精度除法。
    • 高精度乘法 + 比较:通过高精度乘法计算 \(M \times mid\),再与 \(N\) 逐位比较,实现精确判断。
    • 通用性强:此方法不要求除数 \(M\) 为普通整数,可直接处理两个高精度数相除,只需实现高精度乘高精度即可(代码中为乘低精度,但思想可扩展)。
    • 适用于大整数除法:当被除数和除数均超出基本数据类型时,二分+高精度乘法是一种可靠且易于实现的替代方案。

【算法标签】

普及 #高精度

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 20005;
struct BIG
{
    int len, num[N];
    BIG()
    {// 构造函数:将高精度数字初始化为0
        memset(num, 0, sizeof(num));
        len = 1;
    }
    void set(int x)
    {// 将int类型数字x转化为高精度数字
        memset(num, 0, sizeof(num));
        len = 0;
        while (x>0)
        {
            num[++len] = x % 10;
            x /= 10;
        }
        if (len==0) len = 1;  // 特判0的情况
    }
    void set(string s)
    {// 将string类型转化为高精度数字
        memset(num, 0, sizeof(num));
        len = s.size();
        for (int i=1; i<=len; i++)
            num[i] = s[len-i] - '0';
    }
    void print()
    {// 输出高精度数字
        for (int i=len; i>=1; i--)
            cout << num[i];
        cout << endl;
    }
};
BIG operator*(BIG a, int b)
{
    BIG c;
    c.len = a.len;
    int u = 0;
    for (int i=1; i<=c.len; i++)
    {
        int t = a.num[i] * b + u;
        c.num[i] = t % 10;
        u = t / 10;
    }
    while (u>0)
    {
        c.num[++c.len] = u % 10;
        u /= 10;
    }
    return c;
}
BIG A, B, C;
string s1, s2;
signed main() {
    cin >> s1 >> s2;
    A.set(s1);
    B.set(s2);

    int l = 0, r = 1e18;

    while (l < r) {
        int mid = (l + r + 1) / 2;  // 注意:向上取整
        cout << "mid " << mid << endl;
        C = B * mid;
        cout << "c " << endl;
        C.print();

        // 检查 b*mid 是否 ≤ a
        bool condition = true;

        if (C.num[0] > A.num[0]) {
            // b*mid的位数比a多,肯定大于a
            condition = false;
        } else if (C.num[0] < A.num[0]) {
            // b*mid的位数比a少,肯定小于a
            condition = true;
        } else {
            // 位数相同,逐位比较
            bool less = false, greater = false;
            for (int i = C.num[0]; i >= 1; i--) {
                if (C.num[i] < A.num[i]) {
                    less = true;
                    break;
                } else if (C.num[i] > A.num[i]) {
                    greater = true;
                    break;
                }
            }
            // 如果c == a 或 c < a,条件成立
            condition = !greater;  // 不大于就是小于等于
        }

        if (condition) {
            l = mid;  // 可以尝试更大的mid
        } else {
            r = mid - 1;  // 太大了
        }
    }
    cout << l << endl;
    return 0;
}

【运行结果】

1000
333
3
posted @ 2026-09-02 21:40  团爸讲算法  阅读(10)  评论(0)    收藏  举报