[动态规划] 最佳加法表达式

描述

给定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 都装不下的大整数,并用模拟列竖式的办法进行大整数的加法。

解题分析

这同样是一道非常经典的动态规划题目,我们需要去分析放置的加号的位置,然后去求一个最大的结果,本题难度上升的最大一个原因就是我们需要去处理一个高精度运算的问题,这个我们用一个结构体去完成就好了。然后确定一下递推公式,dp[i][j]=max(dp[i][j],dp[j][j-1]+num(j,i-1),dp[j+1][j-1]+num(j+1,i-1),....)。这里dp[i][j]表示考虑前i个数,插入j-1的加号得到的最大值。如果说i<=j,说明不支持那么多加号的插入,这个时候需要注意把此时的dp[i][j]设置为一个很大的数防止后面的过程中取。还有一种特殊情况是插入的加号的数量为0的时候,这个时候我们直接把整个表达式的值作为结果即可。

代码实现
#include <iostream>
#include <cmath>
#include <iomanip>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <list>
#include <bitset>
using namespace std;

struct Bigint{
	char nums[500];
	
	Bigint(const char *s) {
		strcpy(nums,s);
	}
	
	Bigint(){
		strcpy(nums,"0");
	}
	
	Bigint operator+(const Bigint& o){
		int a[500]={0},b[500]={0},c[500]={0};
		int lena=strlen(nums),lenb=strlen(o.nums);
		
		for(int i=0,j=lena-1;i<lena;i++,j--){
			a[i]=nums[j]-'0';
		}
		
		for(int i=0,j=lenb-1;i<lenb;i++,j--){
			b[i]=o.nums[j]-'0';
		}
		int len=max(lena,lenb)+3;
		int tmp=0;
		for(int i=0;i<=len;i++){
			c[i]=a[i]+b[i]+tmp;
			tmp=c[i]/10;
			c[i]%=10;
		}
		int pos=-1;
		for(int i=len;i>=0;i--){
			if(c[i]){
				pos=i;
				break;
			}
		}
		if(pos==-1){
			return Bigint("0");
		}
		else{
			char tmp[500];
			int i,j;
			for(i=0,j=pos;i<=pos;i++,j--){
				tmp[i]=c[j]+'0';
			}
			tmp[i]='\0';
			return Bigint(tmp);
		}
	}
	
	Bigint get(int i,int j){
		char tmp[500];
		int k=0;
		for(int l=i;l<=j;l++){
			tmp[k++]=nums[l];
		}
		tmp[k]='\0';
		return Bigint(tmp);
	}
	
	bool operator<(const Bigint& o) const{
		//cout<<nums<<" vs "<<o.nums<<endl;
		int len1=strlen(nums);
		int len2=strlen(o.nums);
		if(len1==len2){
			for(int i=0;i<len1;i++){
				//cout<<nums[i]<<" "<<o.nums[i]<<endl;
				if(nums[i]==o.nums[i]){
					continue;
				}
				else{
					return nums[i]-'0'<o.nums[i]-'0';
				}
			}
			return false;
		}
		else{
			return len1 < len2;
		}
	}
};

int main(){
	int m;
	int t=0;
	while(cin>>m){
		t++;
		if(t>15) break;
		
		char num[500];
		cin >> num;
		Bigint qwq(num);
		int len=strlen(num);
		Bigint dp[55][55];
		for(int i=1;i<=len;i++){
			for(int j=0;j<=m;j++){
				if(j==0){ 
					dp[i][j] = qwq.get(0,i-1);
				}
				else if(i<=j){
					dp[i][j] = Bigint("999999999999999999999999999999999999999999999999");
				}
				else{
					//cout<<i<<" "<<j<<endl;
					Bigint tmp("999999999999999999999999999999999999999999999999");
					for(int p=j; p<=i-1 ;p++){
						tmp= min( dp[p][j-1] + qwq.get(p,i-1) , tmp );
					}
					dp[i][j]=tmp;
				}
			}
		}
		
		cout<<dp[len][m].nums<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值