leetcode 1000

题目:https://leetcode-cn.com/problems/minimum-cost-to-merge-stones/

区间dp

首先有解的条件为(n-1)%(K-1)==0

dp[i][j][k]表示把 第i堆石头到第j堆石头合并为k堆的最小代价

我们可以枚举中间点m,把左边分为一堆,把右边分为k-1的堆

那么我们有

dp[i][j][k] = min(dp[i][j][k], dp[i][m][1] + dp[m+1][j][k-1])

为什么不能是左边2堆,右边k-2堆呢?考虑左边合并为两堆,那么我们可以找一个点,划到右边,就还是左边一堆,右边k-1堆啦,所以其他所有的情况实际上都已经被 左边1堆,右边k-1堆覆盖了。

还有一个方程就是dp[i][j][1] = dp[i][j][K] + sum[j] - sum[i-1],注意是大写的K,就是说如果有K堆了,那么我们可以把它们合并为一堆,代价为这些石头的总和。

public class Leetcode1000 {
    public static void main(String[] args) {
        int i = new Leetcode1000().mergeStones(new int[]{3, 5, 1, 2, 6}, 3);
        System.out.println(i);

    }


    static int INF = 0x3f3f3f3f;

    public int mergeStones(int[] stones, int K) {
        int n = stones.length;
        int[] pre = new int[n + 1];
        int[] a = new int[n + 1];
        //下标改一下,方便操作
        for (int i = 0; i < n; i++) {
            a[i + 1] = stones[i];
            pre[i + 1] = pre[i] + a[i + 1];
        }
        int[][][] dp = new int[n + 1][n + 1][n + 1];
        if ((n - 1) % (K - 1) != 0) {
            return -1;
        }
        //dp初始状态
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                for (int k = 1; k <= n; k++) {
                    dp[i][j][k] = INF;
                }
            }
        }
        for (int i = 1; i <= n; i++) {
            dp[i][i][1] = 0;
        }
        //区间dp注意最外层枚举len
        for (int len = 2; len <= n; len++) {
            //枚举左端点得到右端点
            for (int l = 1; l + len - 1 <= n; l++) {
                int r = l + len - 1;
                //枚举中点进行状态转移
                for (int m = l; m < r; m++) {
                    //枚举合并成k堆
                    for (int k = 2; k <= len; k++) {
                        dp[l][r][k] = Math.min(dp[l][r][k], dp[l][m][k - 1] + dp[m + 1][r][1]);
                        System.out.printf("dp[%s][%s][%s]=%s", l, r, k, dp[l][r][k]);
                        System.out.println();
                    }
                }
//                System.out.printf("dp[%s][%s][%s]=%s", l, r, K, dp[l][r][K]);
                System.out.println();
                dp[l][r][1] = dp[l][r][K] + pre[r] - pre[l - 1];
//                System.out.printf("dp[%s][%s][%s]=%s", l, r, 1, dp[l][r][1]);
                System.out.println();
            }
        }
        return dp[1][n][1];
    }
}

优化:

1、通过编程可以发现,实际上很多时候dp[i][j][k]是没有值的,比如K=3的时候,dp[1][4][2],把4堆石头每次合并三个,是不可能合并成两堆的。通过这个是可以去优化我们的中间点m的,直接跳过不可能的点。

2、实际上i到j一直合并下去,能合并到的堆数是确定的,可以定义dp[i][j]为合并i到j的最小代价,这样就可以去掉第三维k,这个也是我在视频里看到的优化。

猜你喜欢

转载自blog.csdn.net/dezhonger/article/details/88985067