戳气球 动态规划

1. 题目描述

n个气球,编号为0n-1,每个气球上都标有一个数字,这些数字存在数组nums中。

现在要求你戳破所有的气球。如果你戳破气球i,就可以获得nums[left] * nums[i] * nums[right]个硬币。 这里的leftright代表和i相邻的两个气球的序号。注意当你戳破了气球i后,气球left和气球right就变成了相邻的气球。
求所能获得硬币的最大数量。

示例:

输入: [3,1,5,8]
输出: 167 
解释: nums = [3,1,5,8] --> [3,5,8] -->   [3,8]   -->  [8]  --> []
     coins =  3*1*5      +  3*5*8    +  1*3*8      + 1*8*1   = 167

2. 题解

class Solution {
    public int[][] rec;
    public int[] val;

    public int maxCoins(int[] nums) {
        int n = nums.length;
        val = new int[n + 2];
        for (int i = 1; i <= n; i++) {
            val[i] = nums[i - 1];
        }
        val[0] = val[n + 1] = 1;
        rec = new int[n + 2][n + 2];
        for (int i = 0; i <= n + 1; i++) {
            Arrays.fill(rec[i], -1);
        }
        return solve(0, n + 1);
    }

    public int solve(int left, int right) {
        if (left >= right - 1) {
            return 0;
        }
        if (rec[left][right] != -1) {
            return rec[left][right];
        }
        for (int i = left + 1; i < right; i++) {
            int sum = val[left] * val[i] * val[right];
            sum += solve(left, i) + solve(i, right);
            rec[left][right] = Math.max(rec[left][right], sum);
        }
        return rec[left][right];
    }
}

假设只有一个气球,气球上的数字为8,那么所能获得硬币的最大数量为8
如果有两个气球[3,8],有两种情况:
1、先戳8,后戳3。所能获得硬币为3*8*1 + 1*3*1 = 27
2、先戳3,后戳8。所能获得硬币为1*3*8 + 1*8*1 = 32
因此,所能获得硬币的最大数量为32

LeetCode官方的题解说,戳气球会导致两个气球从不相邻变成相邻,使得后续操作难以处理。
倒过来看这些操作,先戳8后戳3,相当于先加3,后加8

public int solve(int left, int right) {
    if (left >= right - 1) { // 如果区间内无法添加气球,获得的硬币数就为0。比如区间[0,1]
        return 0;
    }
    if (rec[left][right] != -1) {
        return rec[left][right];
    }
    for (int i = left + 1; i < right; i++) {
        int sum = val[left] * val[i] * val[right]; // 添加每个气球获得的硬币数
        sum += solve(left, i) + solve(i, right); // 递归添加气球
        rec[left][right] = Math.max(rec[left][right], sum);
    }
    return rec[left][right];
}

方法solve里面的for循环按顺序遍历数组中的每个元素,表示每个气球都有可能是第一个添加的。添加完第一个气球,剩余的气球都有可能是第二个添加的气球,以此类推,直到将所有气球添加到区间内刚好填满区间。
for循环第二次遍历只是考虑将另一个气球作为第一个添加的气球。

如果有三个气球[3,5,8],有六种情况:

358:1*3*5 + 1*5*8 + 1*8*1 = 63
385:1*3*5 + 5*8*1 + 1*5*1 = 60
538:3*5*8 + 1*3*8 + 1*8*1 = 152
583:3*5*8 + 3*8*1 + 1*3*1 = 147
853:5*8*1 + 3*5*1 + 1*3*1 = 58
835:5*8*1 + 1*3*5 + 1*5*1 = 60

这里的358:表示先戳3,再戳5,最后戳8,相当于先加8,再加5,最后加3

如果有n个气球,就有n!种情况,这属于组合问题。

参考:

原文地址:https://www.cnblogs.com/gzhjj/p/14123378.html