动态规划之最佳加法表达式

动态规划之最佳加法表达式

题目

有一个由1..9组成的数字串.问如果将m个加号插入到这个数字串中,各种可能形成的表达式中,值最小的那个表达式的值是多少?

解析

假设数字串的长度为n,有m个括号,插入的所有可能性为n!/m!。如果要把所有可能性全部计算出来再找出最小值,这样计算的时间复杂符是巨大的。

换一个角度思考,从后往前来看:若最后一个加号添加完毕,由于无论加号的位置怎样变化,最后一个加号后面的数值是一个确定的值,那么整个表达式的最小值就是最后一个加号后面的数值加上加号前面的表达式的最小值。
我们假设有m个数字和n个加号,最后一个加号放在了第i个数字的后面,
再假设m个数字和n个加号组成表达式的最小值为min(m,n),
且第i个数字到第n个数字组成的数值为num(i, n),
则有min(m,n) = num(i+1,m) + min(i, n-1)。而min(i, n-1)可以继续看成是有m-1个数字和i和加号的表达式的最小值。
。。。这是什么?
上述推导式每一层表达式都在求最小的值,而最后的值需要从后往前一层一层分解为一个个子问题,这不正好是动态规划的特点嘛!
所以我们可以采用动态规划的思想来考虑这个问题。
在上面的推导中其实已经给出了动态规划中最关键的状态转移方程,下面我们只需要考虑:它的最原始状态是怎样的?
当只有一个加号时,我们需要将这一个加号在每个数字间放一下,看看最小的放置位置在哪;若没有加号,毫无疑问,最小值就是这n个数字形成的数值。 所以,初始状态也有了:当加号个数n为0时,min(m,n) = num(0,m)。据此,我们就可以写出最佳加法表达式的动态规划解法。

这里需要强调一点,就是在写状态转移方程式要注意i的取值:第i个数字后是最后一个加号。i的最小值为:当前n-1个加号挤在一起,比如1+2+3+4+i,第i个数字前有n-1个数字,此时i对应的是第n个数字;i的最大值为:最后一个加号在最后一个数字前面,此时i对应的是第m-1个数字。由于数学运算法则的约束,加号的个数不能多于数字的个数减一,所以i的循环范围为for(i = n; i<=(m-1); i++)。

解法一:递归

#include<iostream>
using namespace std;

#define MAX_LEN 10
#define INF 2100000000

char a[MAX_LEN];    //存放数字字符串的

int L[MAX_LEN][MAX_LEN];  //存放结果的二维数组

//begin 与 end 均为下标
int my_atoi(char *a, int begin, int end)
{
    int result = 0;
    int level = 1;
    int i = end;
    while(i>=begin)
    {
        result += ((a[i]-'0')*level);
        i--;
        level *= 10;
    }
    return result;
}

int my_min(int a, int b)
{
    return a<b ? a : b;
}

//len---数字的个数    n---加号的个数
int best_addtion_expression(char *a, int len, int n)
{
    if(n == 0)
    {
         L[len-1][n] = my_atoi(a, 0, len-1);
    }

    else if(n >= len)
        L[len-1][n] = INF;
    else
    {
        int t = INF;    
        for(int i = n; i<len; i++)
        {
            int cache = best_addtion_expression(a, i, n-1);
            if (cache != INF)
            {
                t = my_min(t, cache + my_atoi(a, i, len-1));
            }
        }
        L[len-1][n] = t;
    }   
    return L[len-1][n];
}

int main()
{
    int n = 0;
    int result = 0;
    int len = 0;
    cin >> a>>n;
    len = strlen(a);
    result = best_addtion_expression(a, len, n);
    cout << result<< endl;
    return 0;
}

解法二:递推

#include<iostream>
using namespace std;

#define MAX_LEN 6
#define INF 2100000000

char a[MAX_LEN];    //存放数字字符串的

int L[MAX_LEN][MAX_LEN];  //存放结果的二维数组

//begin 与 end 均为下标
int my_atoi(char *a, int begin, int end)
{
    int result = 0;
    int level = 1;
    int i = end;
    while(i>=begin)
    {
        result += ((a[i]-'0')*level);
        i--;
        level *= 10;
    }
    return result;
}

int my_min(int a, int b)
{
    return a<b ? a : b;
}

//BestAdditionExpresion
// len---数字的个数  n---符号的个数
int BAE(char *a, int len, int n)
{
    int i = 0;  //数字个数
    int j = 0;  //加号个数
    int z = 0;
    for (i = 1; i<=len; i++)
    {
        L[i][0] = my_atoi(a, 0, i-1);
        for (j = 1; j<=n; j++)
        {
            if (j > i-1)
            {
                break;
            }
            for (z = j; z<i; z++)
            {
                //z为最后一个加号前的数字的编号
                //i个数字,j个加号组成的值的最小值
                //等于 编号z+1到i的数字组成的数值 加上 L[z][j-1] 的最小值
                L[i][j] = my_min(L[i][j], L[z][j-1]+my_atoi(a, z, i-1));
            }
        }
    }
    return L[len][n];
}

int main()
{
    int n = 0;
    int result = 0;
    int len = 0;
    int i = 0; 
    int j = 0;
    cin >> a>>n;
    len = strlen(a);
    for (i = 0; i<=len; i++)
    {
        for (j = 0; j<=n; j++)
        {
            L[i][j] = INF;
        }
    }
    result = BAE(a, len, n);
    cout << result<< endl;
    return 0;
}
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值