最佳加法表达式

最佳加法表达式

总时间限制:
1000ms
内存限制:
65536kB
描述

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

输入
有不超过15组数据
每组数据两行。第一行是整数m,表示有m个加号要放( 0<=m<=50)
第二行是若干个数字。数字总数n不超过50,且 m <= n-1
输出
对每组数据,输出最小加法表达式的值
样例输入
2
123456
1
123456
4
12345
样例输出
102
579
15
提示
要用到高精度计算,即用数组来存放long long 都装不下的大整数,并用模拟列竖式的办法进行大整数的加法。
来源

Guo Wei


郭老师的一个BT题目。动态规划就算了吧,还要用到高精度计算!做这个题目真的是不会了,c++在这个高精度计算上卡住了,幸亏有java,使用Math包中的BigInteger完成高精度计算,先说说java用到的构造方法和一般方法。

BigInteger(String val)
          将 BigInteger 的十进制字符串表示形式转换为 BigInteger。

 BigIntegeradd(BigInteger val)
          返回其值为 (this + val) 的 BigInteger。
 BigIntegermin(BigInteger val)
          返回此 BigInteger 和 val 的最小值。
再就是郭老师慕课上讲的思路:


其中这个递归函数说明了它所有的跳出情况,下面是自己写的java代码:
import java.util.*;
import java.math.*;
public class Best_add_method {
	static String expression;
	static BigInteger[][] num  = new BigInteger[55][55];
	public static final BigInteger INF = new BigInteger("9999999999999999999999999999999999999999999999999999999");
	static BigInteger V(int m,int n)  
	{  
	    if(m == 0)//无加号  
	        return num[1][n];  
	    else if(n < m+1)//加号过多  
	        return INF;  
	    else  
	    {  
	        BigInteger t = INF;  
	        for(int i = m;i <= n-1;i++)  
	           t = t.min(num[i+1][n].add(V(m-1,i)));	//  V(m-1,i)+num[i+1][n]
	        return t;  
	    }  
	}
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		int m,n;
		while( input.hasNext() )  
	    {  
			m = input.nextInt();
	        expression = input.next();
	        n = expression.length();
	        //预处理,计算i到j数字串组成的数字  
	        for(int i = 1;i <= n;i++)  
	        {  
	        	num[i][i] = new BigInteger(expression.substring(i-1, i));//只有一个数字
	        	//System.out.println(num[i][i]);
	            for(int j = i+1;j <= n;j++)  
	            {  
	                num[i][j] = new BigInteger(expression.substring(i-1, j));
	                //System.out.println(num[i][j]);
	            }  
	        }  
	        System.out.println(V(m,n));
	    }
	}
}

下面是后台的恶心数据:

in:
0
1234567891234567891233445
2
123456
1
123456
4
12345
40
259374238584359484875958348749595954893736597
14
274238584359484875958348749595954893736597
44
1111274238584359484875958348749595954893736597
46
111127423858435948487595834874959595489373659787
8
1111274238
1
111127423858435948487595834874959595489373659787
2
111127423858435948487595834874959595489373659787
3
111127423858435948487595834874959595489373659787
13
111127423858435948487595834874959595489373659787

out:
1234567891234567891233445
102
579
15
354
6511
260
275
39
946002383453925322147382
15555523195731641
1871324530835
25259

知道大部分人是来看C或C++的代码的,下面是从讨论区拿来的代码,是一位大神,真的感谢他!(下面不是我自己写的,是复制的。。。。。)

#include<bits/stdc++.h>
#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();
        for (int i=0; i<n; i++)
        {
            num[i+1][i+1] = str.substr(i,1);
        }
        for (int i=1; i<=n; i++) //求解对应的num[i][j]
        {
            for (int j=i+1; j<=n; j++)
            {
                num[i][j] = str.substr(i-1,j-i+1);
            }
        }
        //当加号数目为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;
}

感谢这位大神无私地分享了自己的代码!


  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值