动态规划练习题汇总

描述
有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。
输入
序列n,序列值表示第i堆石子的数量,0<=i<n,如:[4,2,3,9,6,8,1,7]
输出
输出总代价的最小值


1 思路
最先合并的石子堆在后期中可能被多次合并,因此最先合并的石子堆数量越小越好

2 拆分子问题
每次进行合并,需要从当前的石子堆中选取两个相邻的石子数量和最少的作为被合并的堆

3 计算
令第i次合并后的总代价为C(i),C(0)=0,则C(i+1)=C(i)+min{序列[k]+序列[k+1]},其中 0<=i<n-1,0<=k<n-1-i

4 代码
bottom-up DP

const stoneArray = [4, 2, 3, 9, 6, 8, 1, 7];
class CalStone {
  constructor(options) {
    this.stoneArray = Array.isArray(options) ? options : [];
  }
  getStoreBottomUp() {
    let i = 0;
    const len = this.stoneArray.length;
    while (++i < len) {
      let min = { sum: 99999999999, index: [-1, -1] };
      for (let i = 0; i < this.stoneArray.length - 1;i++){
        const sum = this.stoneArray[i] + this.stoneArray[i + 1];
        min = sum < min.sum ? { sum: sum, index: [i, i + 1] } : min;
      }
      this.stoneArray = [].concat(this.stoneArray.slice(0, min.index[0]), min.sum, this.stoneArray.slice(min.index[1]+1));
    }
    console.log(`总代价为 ${this.stoneArray[0]}`);
  }
}
new CalStone(stoneArray).getStoreBottomUp();

recursive DP

const stoneArray = [4, 2, 3, 9, 6, 8, 1, 7];
class CalStone {
  constructor(options) {
    this.stoneArray = Array.isArray(options) ? options : [];
  }
  getStoneRecursive(arr = this.stoneArray) {
    if (arr.length === 1) {
      console.log(`总代价为 ${arr[0]}`);
      return arr;
    } else {
      let min = { sum: 99999999999, index: [-1, -1] };
      for (let i = 0; i < arr.length - 1; i++) {
        const sum = arr[i] + arr[i + 1];
        min = sum < min.sum ? { sum: sum, index: [i, i + 1] } : min;
      }
      const newStoneArr = [].concat(arr.slice(0, min.index[0]), min.sum, arr.slice(min.index[1] + 1));
      return this.getStoneRecursive(newStoneArr);
    }
  }
}
new CalStone(stoneArray).getStoneRecursive();

5 时间复杂度
需要进行n-1次合并,第i次合并需要进行n-i次比较,故时间复杂度为O(n2)


小利子
118 声望6 粉丝