20 打气球的最大分数
Table of Contents

ac

给定一个数组arr,长度为n。代表排有分数的气球。 每打爆一个气球都能获得分数,假设打爆气球的分数为X,获得分数的规则如下:
1)如果被打爆气球的左边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为L:如果被打爆气球的右边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为R.获得分数为L*X*R
2)如果被打爆的气球的左边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为L:如果被打爆气球的右边所有气球都已经被打爆,获得分数为L*X
3)如果被打爆气球的左边所有的气球都已经被打爆:如果被打爆气球的右边有没被打爆的气球,找到离被打爆气球最近的气球。获得分数为X*R.
4)如果被打爆气球的左边和右边所有的气球都已经被打爆。获得分数为X
目标是打爆所有气球,获得每次打爆的分数。通过选择打爆气球的顺序,可以得到不同的总分,请返回能获得的最大分数。

时间复杂度O(n^{3}),空间复杂度O(n^{2})

四种情况:以是否有没有被打爆的最近的气球为标准

某个气球被打爆了,剩下的求解就与该气球无关了

dp[i...j]表示arr[i..j]的最大得分:O(n^2)时间复杂度

如何求dp[i...j+1]

arr[j+1] 可以在arr[i...j]的顺序中任意一次被打爆:这是O(n)

3 2 5

左右补充1: 1 3 2 5 1

第一步:(必须考虑左右情况)
dp[1][1]=3*2=6, dp[2][2]=3*2*5=30,dp[3][3]=2*5=10

第二步:(必须考虑左右情况)
dp[1][2] = dp[2][2] + 再处理1 = 30 + 15 = 45
         = dp[1][1] + 再处理2 = 6 + 10 = 16
         = 45

dp[2][3] = dp[2][2] + 再处理3 = 30 + 15 = 45
         = dp[3][3] + 再处理2 = 10 + 6 = 16
         = 45

第二步:(必须考虑左右情况)
dp[1][3] = dp[1][2] + 再处理3 = 45 + 5 = 50
         = dp[2][3] + 再处理1 = 45 + 3 = 48
         = dp[1][1] + dp[3][3] + 再处理2 = 6 + 10 + 2 = 18
         = 50
import java.util.Scanner;

public class Main {

    public static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {
        int n = sc.nextInt();
        // 方便处理,前后增加一个1
        int[] arr = new int[n+2];
        arr[0] = 1;
        arr[n+1] = 1;
        for(int i=1;i<=n;i++){
            arr[i] = sc.nextInt();
        }
        // 0 1 ... n n+1
        int[][] dp = new int[n+2][n+2];
        // dp[i..i] 的求解就方便了
        for(int i=1;i<=n;i++){
            dp[i][i] = arr[i-1] * arr[i] * arr[i+1];
        }
        // dp[i][j]距离为0的求解了,接下来求距离为1,2...的,即如下3个元素
        // 先求:11 22 33
        // 接着求:12 23
        // 最后求:13 (最后的结果)
        for(int k=1;k<n;k++){
            for(int i=1;i<=n;i++){
                // 求dp[l][r]
                int l = i;
                int r = i + k;
                if(r > n){
                    continue;
                }
                // dp[l][r-1] ... dp[l+1][r]等是已经求出来了的,就是距离是k-1的是已经求解出来了的
                // 这里循环代表,先打爆哪一个
                for(int j=l;j<=r;j++){ // 最后打爆j
                    dp[l][r] = Math.max(dp[l][r],
                            dp[l][j-1] + dp[j+1][r] + arr[l-1] * arr[j] * arr[r+1]);
                }
            }
        }
        System.out.print(dp[1][n]);
    }

}
/*
3
3 2 5

2->1->3  3*2*5+3*5+5=50

8
23 4 45 65 23 43 54 56

639019
 */