OpenJ_Bailian - 4152 :最佳加法表达式

给定n个1到9的数字,要求在数字之间摆放m个加号(加号两边必须有数字),使得所得到的加法表达式的值最小,并输出该值。例如,在1234中摆放1个加号,最好的摆法就是12+34,和为36

Input

有不超过15组数据 
每组数据两行。第一行是整数m,表示有m个加号要放( 0<=m<=50) 
第二行是若干个数字。数字总数n不超过50,且 m <= n-1

Output

对每组数据,输出最小加法表达式的值

Sample Input

2
123456
1
123456
4
12345

Sample Output

102
579
15

Hint

要用到高精度计算,即用数组来存放long long 都装不下的大整数,并用模拟列竖式的办法进行大整数的加法。

 

解题思路:

假定数字串长度是n,添完加号后,表达式的最后 一个加号添加在第 i 个数字后面,那么整个表达式的最小值,就等于在前 i 个数字中插入 m – 1 个加号所能形成的最小值,加上第 i + 1到第 n 个数字所组成的数的值(i从1开始算)。

设V(m,n)表示在n个数字中插入m个加号所能形成 的表达式最小值,那么:

if m = 0

    V(m,n) = n个数字构成的整数

else if   m > n-1

    V(m,n) = ∞

else

    V(m,n) = Min{  V(m-1,i) + Num(i+1,n) } ( i = m … n-1)
Num(i,j)表示从第i个数字到第j个数字所组成的数。数字编号从1开始算。此操 作复杂度是O(j-i+1),可以预处理后存起来。

总时间复杂度:O(mn2) . 

若 n 比较大,long long 不够存放运算过程中的整数,则需要使用高精度计算 (用数组存放大整数,模拟列竖式做加法),复杂度为O(mn3)
 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stdlib.h>

using namespace std;
//定义一些最大量
const int MaxLen = 55;
const string maxv = "999999999999999999999999999999999999999999999999999999999";
string ret[MaxLen][MaxLen];
string num[MaxLen][MaxLen];

int cmp(string &num1,string &num2) //比较大小
{
    int l1 = num1.length();  //先分别求长度
    int l2 = num2.length();
    if (l1 != l2)
    {
        return l1-l2;
    }
    else
    {
        for (int i=l1-1; i>=0; i--)
        {
            if (num1[i]!=num2[i])
            {
                return num1[i]-num2[i];
            }
        }
        return 0;
    }
}

void add (string &num1,string &num2,string &num3)  //大数加法
{
    //加法从低位到高位相加,那么需要将字符串倒过来
    int l1 = num1.length();
    int l2 = num2.length();
    int maxl = MaxLen,c = 0;        //c是进位标志
    for (int i=0; i<maxl; i++)
    {
        int t;
        if (i < l1 && i < l2)
        {
            t = num1[i]+num2[i]-2*'0'+c;
        }
        else if (i < l1 && i >= l2)
        {
            t = num1[i] - '0' + c;
        }
        else if (i >= l1 && i < l2)
        {
            t = num2[i] - '0' + c;
        }
        else
        {
            break;
        }
        num3.append(1,t%10+'0');
        c = t/10;
    }
    while (c)
    {
        num3.append(1,c%10+'0');
        c /= 10;
    }
}


int main()
{
    int m;               //加号数目
    string str;          //输入的字符串
    while(cin >> m >> str)  //多次输入
    {
        //为了之后的加法计算先将这个字符串倒过来
        reverse(str.begin(),str.end());
        
        int n = str.length();//求出长度

        //建立 num[][] 数组
        for (int i=0; i<n; i++) //求解对应的num[i][j]
        {
            for (int j=i; j<n; j++)
            {
                num[i+1][j+1] = str.substr(i,j-i+1);
            }
        }
        
        /*
        if(m == 0)
            return num[1][n];
        else
        {
            long long sum = 9999999999999999;
            for(int i = m; i<=n-1 ;++i)
            {
                long long a = V(m-1,i) + num[i+1][n];
                if(a < sum)
                    sum = a;
            }
            return sum;
        }
        */
        
        //当加号数目为0:即 m 等于 0 时
        for (int i=1; i<=n; i++)
        {
            ret[0][i] = num[1][i];
        }
        
        for (int i=1; i<=m; i++) //对于加号数目的枚举
        {
            for (int j=1; j<=n; j++) //对于长度的枚举
            {
                string minv = maxv;  //确实够大
                string tmp;
                for (int k=i; k<=j-1; k++)
                {
                    tmp.clear(); //清空该串
                    add(ret[i-1][k],num[k+1][j],tmp);//调用函数得到两字符串的和
                    if (cmp(minv,tmp)>0)//进行字符串比较大小
                    {
                        minv = tmp;//字符类型强大如斯,可直接变量赋值
                    }
                }
                ret[i][j] = minv;//求它才是最终目的
            }
        }
        
        //将原先颠倒的字符串倒回来
        reverse(ret[m][n].begin(),ret[m][n].end());
        cout << ret[m][n] << endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朱事顺利、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值