Stone Game 476

Question

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

Solution

这道题既能用DP解,也能用记忆化搜索的方法解。

dp[i][j]表示合并i到j的石头需要的最小代价。

转移函数:

dp[i][j]=dp[i][k]+dp[k+1][j]+sum[i][j] (i<=k<j)。即合并i-j的代价为合并左边部分的代价+合并右边部分的代价+合并左右部分的代价(即i-j所有元素的总和)。找到使dp[i][j]最小的k。

需要初始化sum。DP以长度和不同起点为循环条件,而记忆化搜索需要start和end来确定搜索范围,然后找分割点k,再递归搜索左右部分,有点D&C的味道。

代码如下:

DP

public class Solution {
    /**
     * @param A an integer array
     * @return an integer
     */
    public int stoneGame(int[] A) {
        // Write your code here
        // DP
        if(A == null || A.length == 0){
            return 0;
        }

        int n = A.length;
        int[][] sum = new int[n][n];
        for(int i = 0; i < n; i++){
            sum[i][i] = A[i];
            for(int j = i + 1; j < n; j++){
                sum[i][j] = sum[i][j - 1] + A[j];
            }
        }

        int[][] dp = new int[n][n];
        for(int i = 0; i < n; i++){
            dp[i][i] = 0;
        }

        for(int len = 2; len <= n; len++){
            for(int i = 0; i + len - 1 < n; i++){
                int j = i + len - 1;
                int min = Integer.MAX_VALUE;
                for(int k = i; k < j; k++){
                    min = Math.min(min, dp[i][k] + dp[k + 1][j]);
                }
                dp[i][j] = min + sum[i][j];
            }
        }

        return dp[0][n - 1];
    }
}

Memorized Search

public class Solution {
    /**
     * @param A an integer array
     * @return an integer
     */
    public int stoneGame(int[] A) {
        // Write your code here
        // Memorized search
        if(A == null || A.length == 0){
            return 0;
        }

        int n = A.length;
        int[][] dp = new int[n][n];
        int[] sum = new int[n + 1];

        for(int i = 0; i < n - 1; i++){
            for(int j = i + 1; j < n; j++){
                dp[i][j] = -1;
            }
        }

        sum[0] = 0;
        for(int i = 0; i < n; i++){
            dp[i][i] = 0;
            sum[i + 1] = sum[i] + A[i];
        }

        return search(0, n - 1, sum, dp);
    }

    private int search(int start, int end, int[] sum, int[][] dp){
        if(dp[start][end] >= 0){
            return dp[start][end];
        }

        int min = Integer.MAX_VALUE;
        for(int k = start; k < end; k++){
            int left = search(start, k, sum, dp);
            int right = search(k + 1, end, sum, dp);
            int now = sum[end + 1] - sum[start];
            min = Math.min(min, left + right + now);
        }
        dp[start][end] = min;
        return dp[start][end];
    }
}

results matching ""

    No results matching ""