美文网首页
打气球的最大得分(动态规划)

打气球的最大得分(动态规划)

作者: 迈吉 | 来源:发表于2022-05-08 08:00 被阅读0次

    打气球的最大得分(动态规划)

    1.1. 问题

    给定一个数组arr,表示一排有分数气球。每打爆一个气球都能获得分数,假设所打爆的气球分数为X,那么获得分数的规则如下:

    • 如果被打爆气球的左边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为L;
      如果被打爆气球的右边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为 R。
      获得分数为 L×X×R。
    • 如果被打爆气球的左边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为L;
      如果被打爆气球的右边所有气球都已经被打爆。获得分数为 L×X。
    • 如果被打爆气球的左边所有的气球都已经被打爆;如果被打爆气球的右边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为 R;
      如果被打爆气球的右边所有气球都已经被打爆。获得分数为 X×R。
    • 如果被打爆气球的左边和右边所有的气球都已经被打爆。获得分数为 X。

    目标是打爆所有气球,获得每次打爆的分数。通过选择打爆气球的顺序,可以得到不同的总分,请返回能获得的最大分数。
    要求:时间复杂度O(N3)

    1.2. 解法

    用F(x,y)表示在其他气球都没打爆的情况下,打爆数组arr下标范围为x~y的气球所能得到的最大得分。所以F(0,arr.length-1)即为题目要求的返回结果。
    F(x,y)有以下递推关系:

    F(x,y) = max(arr[x-1]×arr[x]×arr[y+1]+F(x+1,y),arr[x-1]×arr[x+1]×arr[y+1]+F(x,x)+F(x+2,y),..,arr[x-1]×arr[y]×arr[y+1]+F(x,y-1))

    1.3. 代码

    先来递归的实现,注意这里对help数组前后加了两个哨兵节点,用来简化边界处理:

        public static int maxScoreRecursive(int[] arr) {
            int[] help = new int[arr.length + 2];
            help[0] = 1;
            help[help.length - 1] = 1;
            System.arraycopy(arr, 0, help, 1, arr.length);
            return process(help, 1, arr.length);
        }
    
        private static int process(int[] arr, int x, int y) {
            int left = arr[x - 1];
            int right = arr[y + 1];
            if (x == y) {
                return left * arr[x] * right;
            }
            int score = Math.max(process(arr, x + 1, y) + left * arr[x] * right,
                    process(arr, x, y - 1) + left * arr[y] * right);
            for (int i = x + 1; i <= y - 1; i++) {
                score = Math.max(score,
                        process(arr, x, i - 1) + process(arr, i + 1, y) + left * arr[i] * right);
            }
            return score;
        }
    

    一时间没有想到打表的方法怎么做。看了书后有些思路:

    1. 首先通过递归函数的终点条件来设初始值,对本题来说是填对角线。
    2. 然后确定填值的范围,对本题来说是二维数组dp中对角线以上的位置。
    3. 然后确定返回值在dp上的位置,本题是dp[0][arr.length-1],
    4. 然后随机看dp矩阵上一个需要计算的位置,dp[i][j],看计算它时依赖其他的哪些位置,从而确定打表的方向。在本题中,它依赖与同一行左边的位置的值,同一列下边位置的值,因此打表的顺序为从下往上,从左往右(当然先从左往右再从下往上也可以)。
    5. 然后从起始位置开始大表,这个起始位置的判断可以结合第二点、第四点。
        public static int maxScore(int[] arr) {
            int[] help = new int[arr.length + 2];
            help[0] = help[help.length - 1] = 1;
            System.arraycopy(arr, 0, help, 1, arr.length);
    
            int[][] dp = new int[arr.length][arr.length];
            for (int i = 0; i < arr.length; i++) {
                dp[i][i] = help[i] * help[i + 1] * help[i + 2];
            }
            for (int i = arr.length - 2; i >= 0; i--) {
                for (int j = i + 1; j < arr.length; j++) {
                    int score = Math.max(dp[i][j - 1] + help[i] * help[j + 1] * help[j + 2],
                            dp[i + 1][j] + help[i] * help[i + 1] * help[j + 2]);
                    for (int k = i + 1; k <= j - 1; k++) {
                        score = Math.max(score,
                                dp[i][k - 1] + dp[k + 1][j] + help[i] * help[k + 1] * help[j + 2]);
                    }
                    dp[i][j] = score;
                }
            }
            return dp[0][arr.length - 1];
        }
    

    相关文章

      网友评论

          本文标题:打气球的最大得分(动态规划)

          本文链接:https://www.haomeiwen.com/subject/vhgfurtx.html