[lintcode] Stone Game II

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

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

Example 1:

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

Example 2:

Input:
[1, 1, 1, 1]
Output:8
Explanation:
1. Merge first and second piles => [2, 1, 1], score +2
2. Merge the last two piles => [2, 2],score +2
3. Merge the last two piles => [4], score +4

思路:跟 Stone Game I 类似,有点不一样的地方就是array是可以循环取数的,那么解决方法就是copy另外一个数组,B = A + A, 那么就解决了循环的问题;问题转换为,在B数组中,找到长度为n的,最小的cost;

代码跟I一模一样,唯一的区别就是B数组,跟最后的求min;最后比较所有的n长度有效区间谁最小即可,返回该值

public class Solution {
    /**
     * @param A: An integer array
     * @return: An integer
     */
    public int stoneGame2(int[] A) {
        if(A == null || A.length == 0) return 0;
        int n = A.length;
        int[] B = new int[n * 2];
        for(int i = 0; i < 2 * n; i++) {
            B[i] = A[i % n];
        }
        
        int[][] f = new int[2*n][2*n];
        for(int i = 0; i < 2*n; i++) {
            f[i][i] = 0;
        }
        
        // prefix sum;
        int[] sum = new int[2*n];
        for(int i = 0; i < 2*n; i++) {
            sum[i] = i == 0 ? B[0] : sum[i-1] + B[i];
        }
        
        for(int len = 2; len <= n; len++) {
            for(int i = 0; i + len -1 < 2 * n; i++) {
                int j = i + len - 1;
                f[i][j] = Integer.MAX_VALUE;
                for(int k = i; k < j; k++) {
                    f[i][j] = Math.min(f[i][j], f[i][k] + f[k+1][j] + 
                    (i == 0 ? sum[j] : sum[j] - sum[i-1]));
                }
            }
        }

        //最后比较所有的n长度有效区间谁最小即可,返回该值
        int res = Integer.MAX_VALUE;
        for(int i = 0; i < n; i++) {
            res = Math.min(res, f[i][i+n-1]);
        }
        return res;
    }
}

Prefix Sum 数组S也可以用2*n+1来表示,那么Sum[i...j] = S[j+1] - S[i];

因为如下推倒:

        // S[j] = A[0] + .. + A[j-1];
        // S[j+1] = A[0] + ...A[i-1] + A[i]+ ... + A[j];
        // S[i] = A[0] + ... + A[i-1];
        // S[j+1] - S[i] = A[i] + .. + A[j];
        // i, j是A数组里面的i,j,所以后面的式子是成立的;

public class Solution {
    /**
     * @param A: An integer array
     * @return: An integer
     */
    public int stoneGame2(int[] A) {
        if(A == null || A.length == 0) return 0;
        int n = A.length;
        int[] B = new int[n * 2];
        for(int i = 0; i < 2 * n; i++) {
            B[i] = A[i % n];
        }
        
        int[][] f = new int[2*n][2*n];
        for(int i = 0; i < 2*n; i++) {
            f[i][i] = 0;
        }
        
        // prefix sum;
        int[] S = new int[2*n+1];
        S[0] = 0;
        for(int i = 1; i <=2*n; i++){
            S[i] = S[i-1] + B[i-1];
        }
        
        for(int len = 2; len <= n; len++) {
            for(int i = 0; i + len -1 < 2 * n; i++) {
                int j = i + len - 1;
                f[i][j] = Integer.MAX_VALUE;
                for(int k = i; k < j; k++) {
                    f[i][j] = Math.min(f[i][j], f[i][k] + f[k+1][j] + S[j+1] - S[i]);
                }
            }
        }
        
        //最后比较所有的n长度有效区间谁最小即可,返回该值
        int res = Integer.MAX_VALUE;
        for(int i = 0; i < n; i++) {
            res = Math.min(res, f[i][i+n-1]);
        }
        return res;
    }
}

 

你可能感兴趣的:(Dynamic,Programming,PrefixSum)