Problem

There is a stone game.At the beginning of the game the player picks n piles of stones in a line.

The goal is to merge the stones in one pile observing the following rules:

At each step of the game,the player can merge two adjacent piles to a new pile.
The score is the number of stones in the new pile.
You are to determine the minimum of the total score.

Example
For [4, 1, 1, 4], in the best solution, the total score is 18:

1. Merge second and third piles => [4, 2, 4], score +2
2. Merge the first two piles => [6, 4],score +6
3. Merge the last two piles => [10], score +10

Other two examples:
[1, 1, 1, 1] return 8
[4, 4, 5, 9] return 43

思路

  • 画搜索树,
  • 图左边
    • 按照每个合并的可能性来, 从而合并成大的数值. 这种方法即使用记忆化搜索, 能够合并的状态也很小
    • 所以我们不直接将两个值合并, 而是将连续的值划分成一个一个区间, 这样子用区间作为状态, 相同的区间便能很好地进行记忆化
  • 图右边

    • 合并区间, 对这区间合并的花费进行记忆化.
    • 从大到小思考, 先考虑最后的 0 到 n - 1 的总花费 是由哪些小区间组成的

  • 为什么要用区间型dp ?

    • because we can merge two stone at any interval in the array, we need two indexes to record where this interval begins and ends.
  • state

    • dp[i]:代表把第i个到第j个石子合并到一起的最小花费
  • function

    • dp[i][j] = min{ dp[i][k] + dp[k + 1][j] + sum[i][j] } i <= k <= j
  • initialize (这是我出错的地方, 初始化不应该是stone[i], 而是0. 原因是 第i个和第i个合并花费就是0)

    • dp[i][i] = 0
    • i <= j
  • answer

    • dp[0][n - 1]
  • 注意:

    • 同样需要对sum进行区间型动态规划, i <= j,
    • 这道题不能用for循环来做, 因为蓝色依赖于绿色
    public int stoneGame(int[] A) {
        if (A == null || A.length == 0) {
            return 0;
        }
        int len = A.length;
        boolean[][] visited = new boolean[len][len];
        int[][] dp = new int[len][len];
        int[][] sum = new int[len][len];
        for (int i = 0; i < len; i++) {
            sum[i][i] = A[i];
            for (int j = i + 1; j < len; j++) {
                sum[i][j] = sum[i][j - 1] + A[j];
            }
        }

        return getScorefrom(A, 0, len - 1, dp, visited, sum);
    }

    private int getScorefrom(int[] A, int i, int j, int[][] dp, boolean[][] visited, int[][] sum) {
        if (visited[i][j]) {
            return dp[i][j];
        }

        visited[i][j] = true;

        if (i == j) {
            dp[i][j] = 0;
        } else {
            dp[i][j] = Integer.MAX_VALUE;
            for (int k = i; k < j; k++) {
                dp[i][j] = Math.min(dp[i][j], getScorefrom(A, i, k, dp, visited, sum) 
                                    + getScorefrom(A, k + 1, j, dp, visited, sum)
                                    + sum[i][j]);
            }
        }
        return dp[i][j];
    }

results matching ""

    No results matching ""