百练oj:最佳加法表达式,超过了long long范围,用自定义Bigint类解决

本文介绍了一个处理大整数加法运算的方法,并通过动态规划解决在给定数字串中插入加号以获得最小结果的问题。文章提供了一种自定义的大整数类实现,包括加法运算符重载、比较大小等功能,并使用动态规划算法求解最优解。

题目链接:http://cxsjsxmooc.openjudge.cn/2021t2summer/014/
按照老师的思路写了代码,思路详见代码注释

#include <bits/stdc++.h>
#define mem(a, n) memset(a, n, sizeof(a))
#define max_len 52 //bigint类中最多储存位数+1
using namespace std;
//只支持正数和加法操作
class bigint
{
private:
    //数字采用右对齐方式,即最小位在num[max_len-1],前面以0填充
    int num[max_len];
    int len;//表示num中最左端数字的下标

public:
    void init()//初始化函数
    {
        len=max_len;
        mem(num,0);
    }
    bigint(){init();};

    bigint(char const s[])
    {
        init();
        for (int i = strlen(s) - 1; i >= 0; i--)
            num[--len] = s[i] - 48;
    }
    //重载加法运算
    bigint operator+(const bigint &b)
    {
        bigint result;
        int carry = 0; //表示进位
        int l = len < b.len ? len : b.len;//取两数中较大那个数的最左端下标
        for (int i = max_len - 1; i >= l; i--)//从右往左运算
        {
            result.num[i] = num[i] + b.num[i] + carry;
            if (result.num[i] > 9)
            {
                result.num[i] -= 10;
                carry = 1;
            }
            else
                carry = 0;
        }
        //判断最左端一位是否需要进一
        if (carry == 1)
            result.num[--l] = 1;
        result.len = l;
        return result;
    }
    //重载输出符号
    friend ostream &operator<<(ostream &out, const bigint b)
    {
        for (int i = b.len; i <= max_len - 1; i++)
        {
            out << b.num[i];
        }
        return out;
    }
    //重载输出符号
    friend istream &operator>>(istream &in, bigint &b)
    {
        b.init();
        char s[max_len];
        in >> s;
        b = bigint(s);
        return in;
    }
    //重载小于运算符
    bool operator<(const bigint &b)
    {
        if (len < b.len)
            return false;
        if (len > b.len)
            return true;
        for (int i = len; i <max_len; i++)
        {
            if (num[i] > b.num[i])
                return false;
            if (num[i] < b.num[i])
                return true;
        }
        return false;
    }
    //重载大于运算符
    bool operator>(const bigint &b)
    {
        if (len > b.len)
            return false;
        if (len < b.len)
            return true;
        for (int i = len; i <max_len; i++)
        {
            if (num[i] < b.num[i])
                return false;
            if (num[i] > b.num[i])
                return true;
        }
        return false;
    }
    //取x到y位之间的数,这里的xy以正常顺序来计算
    //x=1表示最高位
    bigint subnum(int x, int y) 
    {
        bigint result;
        //将x,y转换为类内数字存储的顺序
        x = x + len - 1;
        y = y + len - 1;
        for (int i = y; i >= x; i--)
        {
            result.num[--result.len] = num[i];
        }
        return result;
    }
    //将数字设置为很大(用于找最小值)
    void set_inf()
    {
        len=1;
        num[len]=9;
    }
    //返回数字位数
    int size()
    {
        return max_len - len;
    }
};
bigint dp[max_len][max_len];  //dp[x][y]在前x个数字中插入y个加号的最小结果
bigint num[max_len][max_len]; //num[x][y]保存输入中x-y位之间的数字组成的数

//将输入数字的各段取出数字存入num数组中
void sub_num(bigint &in)
{
    for (int i = 1; i <= 50; i++)
    {
        for (int j = i; j <= 50; j++)
        {
            num[i][j] = in.subnum(i, j);
        }
    }
}

void solve(int len, int n)
{
    for (int i = 1; i <= len; i++)
    {
        dp[i][0] = num[1][i];
    }
    //i表示加号个数
    for (int i = 1; i < n; i++)
    {
        //j表示前j个数字中插入i-1个+号
        for (int j = i+1; j <= len; j++)
        {
            dp[j][i].set_inf();
            //k表示分割点
            for (int k = i; k < j; k++)
            {
                if (dp[j][i] > dp[k][i - 1] + num[k + 1][j])
                    dp[j][i] = dp[k][i - 1] + num[k + 1][j];
            }
        }
    }
    //最后一步的时候无需将所有长度都计算出,只需计算dp[len][n]即可
    dp[len][n].set_inf();
    for (int k = n; k < len; k++)
    {
        if (dp[len][n] > dp[k][n - 1] + num[k + 1][len])
            dp[len][n] = dp[k][n - 1] + num[k + 1][len];
    }
    //将结果输出
    cout<<dp[len][n]<<endl;
}

int main()
{
    int n;
    while (cin >> n)
    {
        bigint in;
        cin >> in;
        if(n)
        {
            sub_num(in);
            solve(in.size(), n);
        }
        else cout<<in<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值