最佳加法表达式(修改为高精度)

最佳加法表达式

忽然发现部分题目要求高精度

题目描述
有一个由1…9组成的数字串.问如果将m个加号插入到这个数字串中,在各种可能形成的表达式中,值最小的那个表达式的值是多少。
输入:
5 3
1 2 3 4 5
输出:
24

我们一开始可以采用递归的思路来进行思考。
我们先考虑,最右边的加号放在哪里?

假设数字字符2长度为n,最右边的加号放在第i个数字的后边.
那么现在最小值就是在前i个数字中插入m-1个加号后所能形成的最小值.
那么现在就是减而治之的递归思路就完成了(分解为规模更小的子问题来解决)

这也恰恰满足我们的最优子结构和无后效性

V(m,n)表示前n个数组当中插入m个加号所形成表达式最小值

下面借用以下人家的分析:
设V(m,n)表示在n个数字中插入m个加号所能形成
的表达式最小值,那么:
if m = 0
V(m,n) = n个数字构成的整数
else if n < m + 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),为了节省时间我们预处理num(i,j)(就是什么都不干之前把这些值全部算出来)
总时间复杂度:O(mn^2) .

有的题目需要用到高精度模板来进行计算了(long long无法承受得住的时候)高精度模板可以参考我教主的序列的文章

这里我想说的是,你一旦用高精度模板,那么将会变成O(mn^3),应为多了n个元素的数组,你每次操作数组都是O(n)的

下面两个为非高精度的代码
递归型代码

#include <iostream>//最佳加法表达式
#include <cstring>
#include <algorithm>
#include  <cstdio>
const int INF=9999999;
int a[1010],num[1010][1010];
using namespace std;

int V(int m,int n)
{//状态转移方程
    if(m==0)
        return num[1][n];
    else if(n<m+1)//加号多了
        return INF;
    else  
    {  
        int t = INF;  
        for(int i = m;i <= n-1;i++) 
           t = min(t, V(m-1,i)+num[i+1][n]); 
        return t;  
    }  
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))//就是当没有输入的时候退出循环
    {
        for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
        for(int i=1;i<=n;++i)//预处理,节省时间
        {
            num[i][i]=a[i];//只有一个数组
            for(int j=i+1;j<=n;++j)
            {
                num[i][j]=num[i][j-1]*10+a[j];
            }
        }
        cout<<V(m,n)<<endl;

    }
    return 0;
}

递推型的可以自己想一下哦

递推型

#include<iostream>
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
using namespace std;
const int INF=0x3f3f3f3f;//无穷大
const int N=1010;
int a[N],num[N][N],dp[N][N];
//a[N]里面是存数字串
//num[i][j]表示数字串a[N]的第i位到第j位之间的数字串表示的数组
int main(){
    int n,m;
    while(scanf("%d %d",&n,&m)){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        //预处理,计算i到j数字串组成的数字 
        for(int i=1;i<=n;i++){
            num[i][i]=a[i];//只有一个数字 
            for(int j=i+1;j<=n;j++){
                num[i][j]=num[i][j-1]*10+a[j];
            } 
        }
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<=n;i++){
            dp[0][i]=num[1][i];//无加号时 
        }
        //这里n可以写在m前面。要加一个限制条件n>m,好麻烦,所以m在前且n=m+1
        //这里k的取值范围就是m到n,k表示在第k个数后面插入加号 
 for(int i=1;i<=m;i++)
            for(int j=i;j<=n;j++)
                for(int k=i;k<=j;k++)
                    dp[i][j]=min(dp[i][j],dp[i-1][k]+num[k+1][j]); 
        cout<<dp[m][n]<<endl; 
    }
}

下面提供2个高精度的AC代码

//By Guo Wei
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
struct BigInt
{

	int num[110];
	int len;
	BigInt operator+(const BigInt & n) { //重载+,使得 a + b在 a,b都是 BigInt变量的时候能成立
		int ml = max(len,n.len);
		int carry = 0; //进位 
		BigInt result;
		for(int i = 0;i < ml; ++i) {
			result.num[i] = num[i] + n.num[i] + carry;
			if( result.num[i] >= 10) 	{
				carry = 1;
				result.num[i] -= 10;
			}
			else
				carry = 0;
		}
		if ( carry == 1) {
			result.len = ml + 1;
			result.num[ml] = 1;
		}
		else 
			result.len = ml;
		return result;
	}
	bool operator<(const BigInt & n) {
		if( len > n.len )
			return false;
		else if( len < n.len)
			return true;
		else {
			for(int i = len -1; i >= 0; -- i) {
				if( num[i] < n.num[i])
					return true;
				else if( num[i] > n.num[i])
					return false;
			}
			return false;
		}
	}
	BigInt() {
		len = 1;
		memset(num,0,sizeof(num));
	}
	BigInt(const char * n,int L) { //由长度为L的char数组构造大整数。n里面的元素取值范围从 1-9。 
		memset(num,0,sizeof(num));
		len = L;
		for(int i = 0; n[i]; ++i)
			num[len-i-1] = n[i] - '0';
	}
};
ostream & operator <<(ostream & o,const BigInt & n) 
{
	
	for(int i = n.len - 1;i >= 0; --i)
		o << n.num[i];
	return o;
}
const int MAXN  = 60;
char a[MAXN];
BigInt Num[MAXN][MAXN];//Num[i][j]表示从第i个数字到第j个数字所构成的整数 
BigInt V[MAXN][MAXN]; //V[i][j]表示i个加号放到前j个数字中间,所能得到的最佳表达式的值。 
int main()
{
	int m,n;
	BigInt inf; //无穷大 
	inf.num[MAXN-2] = 1;
	inf.len = MAXN-1;

	while(cin >> m ) {
		cin >> a+1;
		n = strlen(a+1);
		for(int i = 1;i <= n; ++i) 
			for(int j = i;j<= n; ++j) {
				Num[i][j] = BigInt(a+i,j-i+1);  
			}
		for(int j = 1; j <= n; ++j)  {
			V[0][j] = BigInt(a+1,j);
		}
		
		for(int i = 1;i <= m; ++i) {
			for(int j = 1; j <= n; ++j) {
				if( j - 1 < i) 
					V[i][j] = inf;
				else {
					BigInt tmpMin = inf;
					for(int k = i; k < j; ++k) {
						BigInt tmp = V[i-1][k] + Num[k+1][j]; 
						if (tmp < tmpMin)
							tmpMin = tmp;
					}
					V[i][j] = tmpMin;
				}
			}
		}
		cout << V[m][n] << endl;
	}
	return 0;
}

之前讲过string,就用string来写写吧

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#define ME0(X) memset((X), 0, sizeof((X)))
using namespace std;
const int L=100;
string dp[100][100];
string add(string a,string b)//只限两个非负整数相加
{
    string ans;
    int na[L]= {0},nb[L]= {0};
    int la=a.size(),lb=b.size();
    for(int i=0; i<la; i++)
        na[la-1-i]=a[i]-'0';
    for(int i=0; i<lb; i++)
        nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    for(int i=0; i<lmax; i++)
        na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10;
    if(na[lmax])
        lmax++;
    for(int i=lmax-1; i>=0; i--)
        ans+=na[i]+'0';
    return ans;
}
string mins(string a,string b)//判断大小
{
    if(a.length()<b.length())
        return a;
    else if(b.length()<a.length())
        return b;
    else
        return a<b?a:b;
}
int main()
{
    int m;
    string s;
    while(cin >> m >> s)
    {
        s=" "+s;
        int len=s.length();
        for(int i=0; i<=len; i++)
            dp[i][0]=s.substr(1,i);
        for(int j=1; j<=m; j++)
        {
            for(int i=0; i<=len; i++)
            {
                for(int x=j; x<i; x++)
                {
                  前x个数和"+"相等时,显然不成立,x个数最多有x-1个"+",所以要单独处理
                    if(x==j)
                        dp[i][j]=add(dp[x][j-1],s.substr(x+1,i-x));
//                    其他的情况,状态转移方程即可
                    else
                        dp[i][j]=mins(dp[i][j],add(dp[x][j-1],s.substr(x+1,i-x)));
                }
            }
        }
        cout << dp[len][m] << endl;
    }
}

学会程序和算法,走遍天下都不怕

在这里插入图片描述广西桂林

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值