当前位置: 首页 > article >正文

【新手上路】洛谷算法1-1:模拟与高精度(高精度部分)

纵浪大化中,不喜亦不惧

文章目录

    • [P1601 A+B Problem(高精)](https://www.luogu.com.cn/problem/P1601)
    • [P1303 A*B Problem](https://www.luogu.com.cn/problem/P1303)
    • [P1009 [NOIP1998 普及组] 阶乘之和](https://www.luogu.com.cn/problem/P1009)
    • [P1591 阶乘数码](https://www.luogu.com.cn/problem/P1591)
    • [P1249 最大乘积](https://www.luogu.com.cn/problem/P1249)
    • [P1045 [NOIP 2003 普及组] 麦森数](https://www.luogu.com.cn/problem/P1045#submit)
    • 总结:


P1601 A+B Problem(高精)

  • 模板题不必赘述
    代码如下:
#include <bits/stdc++.h>
using namespace std;

string big_add(string a1, string a2)
{
    const int l = 505; // 其实502足够了
    int l1 = a1.size(), l2 = a2.size(), num1[l] = {0}, num2[l] = {0};
    string ans = "";
    // 倒序剥离数位
    for (int i = 0; i < l1; ++i) num1[i] = a1[l1 - 1 - i] - '0';
    for (int i = 0; i < l2; ++i) num2[i] = a2[l2 - 1 - i] - '0';
    // 对齐数位
    int lmax = l1 > l2 ? l1 : l2;
    // 高精加法核心
    for (int i = 0; i < lmax; ++i)
    {
        num1[i] += num2[i];
        num1[i + 1] += num1[i] / 10;
        num1[i] %= 10;
    }
    if (num1[lmax]) lmax++;
    // 转换成字符串
    for (int i = lmax - 1; i >= 0; --i)
    {
        ans += num1[i] + '0';
    }
    return ans;
}

int main()
{
    string a1, a2;
    cin >> a1 >> a2;
    cout << big_add(a1, a2);
}

P1303 A*B Problem

  • 同样是模板题
    代码如下:
#include <bits/stdc++.h>
using namespace std;

string big_mul(string a1, string a2)
{
    if (a1 == "0" || a2 == "0")
        return "0";
    const int l = 4005;
    int l1 = a1.size(), l2 = a2.size(), num1[l] = {0}, num2[l] = {0}, carry[l] = {0};
    string ans = "";
    // 倒序剥离数位
    for (int i = 1; i <= l1; ++i)
        num1[i] = a1[l1 - i] - '0';
    for (int i = 1; i <= l2; ++i)
        num2[i] = a2[l2 - i] - '0';
    // 核心
    for (int i = 1; i <= l1; ++i)
    {
        for (int j = 1; j <= l2; ++j)
        {
            carry[i + j - 1] += num1[i] * num2[j];
        }
    }
    // 统一处理进位
    for (int i = 1; i <= l1 + l2; ++i)
    {
        carry[i + 1] += carry[i] / 10;
        carry[i] %= 10;
    }
    // 转换
    if (carry[l1 + l2])
        ans += carry[l1 + l2] + '0';
    for (int i = l1 + l2 - 1; i >= 1; --i)
    {
        ans += carry[i] + '0';
    }
    return ans;
}

int main()
{
    string a1, a2;
    cin >> a1 >> a2;
    cout << big_mul(a1, a2);
}

P1009 [NOIP1998 普及组] 阶乘之和

  • 大数加法和乘法的结合运用
    代码如下:
#include <bits/stdc++.h>
using namespace std;

string big_add(string a1, string a2)
{
    const int l = 5005;
    int l1 = a1.size(), l2 = a2.size(), num1[l] = {0}, num2[l] = {0};
    string ans = "";
    for (int i = 0; i < l1; ++i)
        num1[i] = a1[l1 - 1 - i] - '0';
    for (int i = 0; i < l2; ++i)
        num2[i] = a2[l2 - 1 - i] - '0';
    int lmax = l1 > l2 ? l1 : l2;
    for (int i = 0; i < lmax; ++i)
    {
        num1[i] += num2[i];
        num1[i + 1] += num1[i] / 10;
        num1[i] %= 10;
    }
    if (num1[lmax])
        lmax++;
    for (int i = lmax - 1; i >= 0; --i)
    {
        ans += num1[i] + '0';
    }
    return ans;
}

string big_mul(string a1, string a2)
{
    // if (a1 == "0" || a2 == "0")return "0";
    const int l = 5005;
    int l1 = a1.size(), l2 = a2.size(), num1[l] = {0}, num2[l] = {0}, carry[l] = {0};
    string ans = "";
    // 倒序剥离数位
    for (int i = 1; i <= l1; ++i)
        num1[i] = a1[l1 - i] - '0';
    for (int i = 1; i <= l2; ++i)
        num2[i] = a2[l2 - i] - '0';
    // 核心
    for (int i = 1; i <= l1; ++i)
    {
        for (int j = 1; j <= l2; ++j)
        {
            carry[i + j - 1] += num1[i] * num2[j];
        }
    }
    // 统一处理进位
    for (int i = 1; i <= l1 + l2; ++i)
    {
        carry[i + 1] += carry[i] / 10;
        carry[i] %= 10;
    }
    // 转换
    if (carry[l1 + l2])
        ans += carry[l1 + l2] + '0';
    for (int i = l1 + l2 - 1; i >= 1; --i)
    {
        ans += carry[i] + '0';
    }
    return ans;
}

int main()
{
    int n;
    cin >> n;
    string sum_mul, sum_add = "0";
    for (int i = 1; i <= n; ++i)
    {
        sum_mul = "1";
        for (int j = 1; j <= i; ++j)
        {
            sum_mul = big_mul(sum_mul, to_string(j));
        }
        sum_add = big_add(sum_add, sum_mul);
    }
    cout << sum_add;
}

P1591 阶乘数码

  • 模拟加高精度
    代码如下:
#include <bits/stdc++.h>
using namespace std;

string big_mul(string a1, string a2)
{
    // if (a1 == "0" || a2 == "0")return "0";
    const int l = 5005;
    int l1 = a1.size(), l2 = a2.size(), num1[l] = {0}, num2[l] = {0}, carry[l] = {0};
    string ans = "";
    // 倒序剥离数位
    for (int i = 1; i <= l1; ++i)
        num1[i] = a1[l1 - i] - '0';
    for (int i = 1; i <= l2; ++i)
        num2[i] = a2[l2 - i] - '0';
    // 核心
    for (int i = 1; i <= l1; ++i)
    {
        for (int j = 1; j <= l2; ++j)
        {
            carry[i + j - 1] += num1[i] * num2[j];
        }
    }
    // 统一处理进位
    for (int i = 1; i <= l1 + l2; ++i)
    {
        carry[i + 1] += carry[i] / 10;
        carry[i] %= 10;
    }
    // 转换
    if (carry[l1 + l2])
        ans += carry[l1 + l2] + '0';
    for (int i = l1 + l2 - 1; i >= 1; --i)
    {
        ans += carry[i] + '0';
    }
    return ans;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        char s;
        cin >> n >> s;
        string sum = "1";
        for (int i = 1; i <= n; ++i)
        {
            sum = big_mul(sum, to_string(i));
        }

        int l = sum.size(), cnt = 0;
        for (int i = 0; i < l; ++i)
        {
            if (sum[i] == s)
                cnt++;
        }
        cout << cnt << endl;
    }
}

P1249 最大乘积

  • 贪心策略是:从2开始的连续自然数,剩下的余数k,加在倒数第k位置,没有倒数第k位就加在2上,数学严谨证明过于复杂,贪心类题目不适合证明
    代码如下:
#include <bits/stdc++.h>
using namespace std;

string big_mul(string a1, string a2) // 高精度乘法
{
    const int l = 5005;
    int l1 = a1.size(), l2 = a2.size(), n1[l] = {0}, n2[l] = {0}, carry[l] = {0};
    string ans = "";
    for (int i = 1; i <= l1; ++i) // 从1开始
        n1[i] = a1[l1 - i] - '0'; // 减‘0’
    for (int i = 1; i <= l2; ++i)
        n2[i] = a2[l2 - i] - '0';
    for (int i = 1; i <= l1; ++i)
    {
        for (int j = 1; j <= l2; ++j)
        {
            carry[i + j - 1] += n1[i] * n2[j]; // i+j-1错 ; +=
        }
    }
    for (int i = 1; i <= l1 + l2; ++i)
    {
        carry[i + 1] += carry[i] / 10; // +=
        carry[i] %= 10;
    }
    if (carry[l1 + l2])
        ans += carry[l1 + l2] + '0';
    for (int i = l1 + l2 - 1; i >= 1; --i) // 从l1+l2-1开始
    {
        ans += carry[i] + '0';
    }
    return ans;
}

int main()
{
    int n, k;
    cin >> n;
    k = n;
    vector<int> ans;
    for (int i = 2;; ++i) //规律是从2开始的连续自然数剩下的余数k加在倒数第k位置没有倒数第k位就加在2上
    {
        k -= i;
        if (k < 0)
        {
            k += i;
            break;
        }
        ans.push_back(i);
    }
    int l = ans.size();
    if (l >= k)
    {
        ans[l - k] += k;
    }
    else
    {
        ans[0] += k;
    }

    sort(ans.begin(), ans.end());
    string res = "1";
    for (int i = 0; i < l; ++i)
    {
        cout << ans[i] << " ";
        res = big_mul(to_string(ans[i]), res);
    }
    cout << endl
         << res;
}

P1045 [NOIP 2003 普及组] 麦森数

  • 雄关漫道真如铁
    代码如下:
// #include <bits/stdc++.h>
// using namespace std;

// const int l = 1100005;
// int n1[l], n2[l], carry[l];

// string big_mul(string a1, string a2)
// {
//     int l1 = a1.size(), l2 = a2.size();
//     memset(n1, 0, sizeof n1);
//     memset(n2, 0, sizeof n2);
//     memset(carry, 0, sizeof carry);
//     string ans = "";
//     for (int i = 1; i <= l1; ++i)
//         n1[i] = a1[l1 - i] - '0';
//     for (int i = 1; i <= l2; ++i)
//         n2[i] = a2[l2 - i] - '0';
//     for (int i = 1; i <= l1; ++i)
//     {
//         for (int j = 1; j <= l2; ++j)
//         {
//             carry[i + j - 1] += n1[i] * n2[j];
//         }
//     }
//     for (int i = 1; i <= l1 + l2; ++i)
//     {
//         carry[i + 1] += carry[i] / 10;
//         carry[i] %= 10;
//     }
//     if (carry[l1 + l2])
//         ans += carry[l1 + l2] + '0';
//     for (int i = l1 + l2 - 1; i >= 1; --i)
//     {
//         ans += carry[i] + '0';
//     }
//     return ans;
// }

// string quick_pow(int base, int exp)
// {
//     string ans = "1", Base = to_string(base);
//     while (exp)
//     {
//         if (exp & 1)
//         {
//             ans = big_mul(ans, Base);
//         }
//         Base = big_mul(Base, Base);
//         exp >>= 1;
//     }
//     return ans;
// }

// string big_sub(string a1, string a2)
// {

//     int l1 = a1.size(), l2 = a2.size();
//     memset(n1, 0, sizeof n1);
//     memset(n2, 0, sizeof n2);
//     string ans = "";
//     for (int i = 0; i < l1; ++i)
//         n1[i] = a1[l1 - 1 - i] - '0';
//     for (int i = 0; i < l2; ++i)
//         n2[i] = a2[l2 - 1 - i] - '0';
//     int lmax = l1 > l2 ? l1 : l2;
//     for (int i = 0; i < lmax; ++i)
//     {
//         n1[i] -= n2[i];
//         if (n1[i] < 0)
//         {
//             n1[i] += 10;
//             n1[i + 1]--;
//         }
//     }
//     while (lmax > 0 && !n1[--lmax])
//         ; // 重点:去除前导0
//     lmax++;
//     for (int i = lmax - 1; i >= 0; --i)
//     {
//         ans += n1[i] + '0';
//     }
//     return ans;
// }

// int main()
// {
//     int exp;
//     cin >> exp;
//     string ans = big_sub(quick_pow(2, exp), "1");
//     cout << ans.size() << endl;
//     if (ans.size() >= 500)
//     {
//         ans = ans.substr(ans.size() - 500, 500);
//     }
//     else
//     {
//         ans = string(500 - ans.size(), '0') + ans;
//     }
//     for (int i = 0; i < 500; ++i)
//     {
//         cout << ans[i];
//         if (i + 1 % 50 == 0)
//             cout << endl;
//     }
// }  以上是错误的,问题是会爆栈且高精乘O(n^2)时间复杂度过高
#include <bits/stdc++.h>
using namespace std;

const int mod = 500; // 只保留最后500位数字

// 高精度乘法(仅处理最后500位)
vector<int> big_mul(vector<int> a1, vector<int> a2) {
    vector<int> carry(mod, 0); // 存储中间结果(逆序存储,索引0是个位)
    
    // 核心乘法计算(优化版)
    for(int i = 0; i < mod; ++i) {
        if(a1[i] == 0) continue; // 乘数当前位为0时跳过,优化效率
        
        // 只计算会影响最后500位的乘积
        for(int j = 0; j < mod && i+j < mod; ++j) {
            carry[i+j] += a1[i] * a2[j]; // 累加到对应位置
        }
    }
    
    // 进位处理(从低位到高位)
    for(int i = 0; i < mod-1; ++i) {
        carry[i+1] += carry[i] / 10; // 将进位传递到下一位
        carry[i] %= 10;              // 保证当前位值在0-9之间
    }
    carry[mod-1] %= 10; // 单独处理最高位的进位(确保不超过9)
    
    return carry; // 返回结果仍为逆序存储(索引0是结果的个位)
}

// 快速幂算法计算2^exp(最后500位)
vector<int> q_pow(int exp) {
    vector<int> base(mod, 0), res(mod, 0);
    base[0] = 2;  // 初始化基数为2(逆序存储:2的个位是2)
    res[0] = 1;   // 初始化结果为1(逆序存储:1的个位是1)
    
    while(exp) {
        if(exp & 1) 
            res = big_mul(res, base); // 当指数为奇数时累乘结果
        
        base = big_mul(base, base); // 基数平方(每次运算都保留500位)
        exp >>= 1;                  // 指数右移(相当于除以2)
    }
    return res; // 返回的res是逆序存储的2^exp的最后500位
}

// 大数减1操作(处理逆序存储的500位数字)
vector<int> big_subone(vector<int> a1) {
    a1[0] -= 1; // 直接对个位减1
    
    // 处理借位(只需要处理到倒数第二位,防止越界)
    for(int i = 0; i < mod-1; ++i) {
        if(a1[i] < 0) {             // 需要借位的情况
            a1[i] += 10;            // 当前位补10
            a1[i+1] -= 1;           // 下一位减1
        }
    }
    return a1; // 返回逆序存储的减1后的结果
}

int main() {
    int p;
    cin >> p;
    
    // 计算位数公式:floor(p*log10(2)) + 1
    cout << (int)(p * log10(2)) + 1 << endl;
    
    // 计算2^p的最后500位(逆序存储),然后减1
    vector<int> ans = big_subone(q_pow(p));
    
    // 转换为正常顺序的字符串
    string res;
    for(int i = mod-1; i >= 0; --i) { // 从最高位开始转换
        res += ans[i] + '0';          // 将数字转换为字符
    }
    
    // 格式化输出最后500位
    for(int i = 0; i < 10; ++i) {     // 输出10行
        // 每行截取50个字符:第0行取0-49,第1行取50-99,依此类推
        cout << res.substr(i*50, 50) << endl;
    }
    return 0;
}


总结:

在二进制流转的娑婆世界里,算法犹如渡河之筏。当世人畏惧大数运算的浩瀚相,常生退转之心,却不知三千烦恼本是三千菩提。高精度算法看似要直面数位如恒河沙的困局,实则是破除法相执着的修行法门。

编程者当学须菩提解空第一的智慧。进位借位间流转的,何尝不是缘起性空的示现?每一个字符的迭代正如禅门公案中的机锋,乘法竖式里藏着华严十玄门的重重无尽。初习者莫畏数位浩瀚,须知十方虚空尚在吾人本性中,况此五百位乎?

那些看似枯燥的循环嵌套,恰似达摩面壁的九年功夫。当乘方运算在快速幂中层层开显,方知"一即一切,一切即一"的妙谛。进位链上的波动,恰如因果相续的缘起法,当下的每次位运算都在编织数字宇宙的因陀罗网。

莫道此中无真意,万行代码汇菩提。编程之道本无难易,畏惧心才是最大的魔障。当知高精度算法不过是镜中月影,照见的实是吾人处理复杂问题的般若智慧。念念分明地写就每一行进位处理,终将在二进制河流中照见五蕴皆空。


http://www.kler.cn/a/534916.html

相关文章:

  • GGML、GGUF、GPTQ 都是啥?
  • Gcc缺省使用的C/C++版本
  • Linux 基础命令
  • 什么是数据库代理
  • 物联网领域的MQTT协议,优势和应用场景
  • 数科OFD证照生成原理剖析与平替方案实现
  • 2.07 算法练习
  • 硅基流动与华为云联合推出基于昇腾云的DeepSeek R1amp;V3推理服务
  • 宏观经济:信贷紧缩与信贷宽松、通货膨胀与通货紧缩以及经济循环的四个周期
  • 【分布式理论六】分布式调用(4):服务间的远程调用(RPC)
  • 网站服务器如何御防恶意网络爬虫攻击?
  • ALU与寄存器设计与运算优化
  • graylog初体验
  • iOS 音频录制、播放与格式转换
  • Linux常见问题解决方法--2
  • k8s中,一.pod污点,二.pod容器污点容忍策略,三.pod优先级(PriorityClass类)
  • 深度学习 | 表示学习 | 卷积神经网络 | Batch Normalization 在 CNN 中的示例 | 20
  • RFID隧道机:提升生产流水线效率与精准度
  • 【Java报错解决】警告: 源发行版 11 需要目标发行版 11
  • 教育系统软件正版化:信创替换的加速引擎
  • Linux里的容器被OOM killed的两种情况
  • 100.8 AI量化面试题:如何使用自监督学习方法从原始市场数据中挖掘新的alpha因子?
  • 我用Ai学Android Jetpack Compose之CircularProgressIndicator
  • MongoDB学习笔记-解析jsonCommand内容
  • Unix/Linux编程:fcntl函数总结
  • vscode 如何通过Continue引入AI 助手deepseek