动态规划

动态规划是一个比较牛掰的算法,它与分治法相似。分治法是从大问题分解成小问题,自顶而下求解,再合并得到原问题的解;动态规划规划也是大问题分解成小问题,但是这些问题之间存在着某种联系,类似于数学归纳,自底向上求解,先求解最小的子问题,把结果存储在表格中,再求解大的子问题时,直接从表格中查询小的子问题的解,避免重复计算,从而提高算法的效率。


动态规划的解题步骤一般分为4步:
  1. 分解最优解的结构特征。
  2. 建立最优值得递归式。
  3. 自底向上计算最优解,并记录。
  4. 构造最优解。

那什么样的问题可以已使用动态规划算法呢?只需要具备以下的两个性质:

  1. 最优子结构。
  2. 子问题重叠。

例题:石子合并

有n堆石子堆放在路边,现要将石子有序地合并成一堆,规定每次只能移动相邻的两堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费(最大或最小)。

eg:我们以最小为例([5,8,6,9,2,3]):

1.分解最优解的结构特征。

假设我们知道了在第k堆的石子分开可以获得最优解,那么原问题就变成了2个子问题a1,a2。只需要证明原问题的最优解包含两个子问题的最优解。

假设n对石子合并起来的最优花费是c,子问题a1的最优解是m,子问题a2的最优解是n,两个子问题的石子数量之和是w(i,j),那么 c=m+n+w(i,j) 。因此只需要证明c是最优的,则a和b一定是最优的。

证明: 如果m不是最优的,那子问题a1一定存在最优解m’,m’<m,那么m’+n+w(i,j) < c,这与我们的假设c是最优解相矛盾,因此c一定是最优的,则m也是最优的。同理可证n也是最优的。

所以原问题的最优解包含了子问题的最优解。

2.建立最优值得递归式。
Min[i][j]表示从第i堆石子到底j堆石子合并的最小花费。

Min[i][j] = { 0 (i=j)
min(Min[i][k] + Min[k+1][j] + sum[j] - sum[i-1]) (i<j)

Min[i][j]t条件
0i=j
min(Min[i][k]+Min[k+1][j]+sum[j]-sum[i-1]) ,i<=k<ji<j

3.自底向上计算最优解,并记录。

01332567184
014375061
0152839
01119
05
0

4.构造最优解。

01332567184
014375061
0152839
01119
05
0
//合并石子
function mergeStones(arr){

    //石子的总数
    let sum = new Array();
    sum[0] = 0;
    for(let  i = 1; i <= arr.length; i++){
        sum[i] = sum [i-1] + arr[i-1];
    }

    //二维数组存储最大,最小值
    let Min = new Array(arr.length+1);
    let Max = new Array(arr.length+1);
    for(let i = 0; i <= arr.length; i++){
        Min[i] = new Array(arr.length+1);
        Max[i] = new Array(arr.length+1);
    }

    //i=j
    for(let i = 1; i <= arr.length; i++){
        Min[i][i] = 0;
        Max[i][i] = 0;
    }

    //i<j
    for(let len = 2; len <= arr.length; len++ ){
        for(let i = 1; i<= arr.length+1-len; i++){
            Max[i][i+len-1] = Max[i][i] + Max[i+1][i+len-1] + sum[i+len-1] - sum[i-1];
            Min[i][i+len-1] = Min[i][i] + Min[i+1][i+len-1] + sum[i+len-1] - sum[i-1];
            for(let k = i+1; k < i+len-1; k++){
                Max[i][i+len-1] = Math.max(Max[i][i+len-1] ,  Max[i][k] + Max[k+1][i+len-1] + sum[i+len-1] - sum[i-1]);
                Min[i][i+len-1] = Math.min(Min[i][i+len-1] , Min[i][k] + Min[k+1][i+len-1] + sum[i+len-1] - sum[i-1]);
            }
        }
    }
console.log('最大:'+Max[1][arr.length])
console.log('最小:'+Min[1][arr.length])
}


mergeStones([5,8,6,9,2,3])
//最大:129
//最小:84
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值