首页 > 试题广场 >

打气球的最大分数

[编程题]打气球的最大分数
  • 热度指数:2203 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定一个数组arr,长度为n。代表排有分数的气球。 每打爆一个气球都能获得分数,假设打爆气球的分数为X,获得分数的规则如下:
1)如果被打爆气球的左边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为L:如果被打爆气球的右边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为R.获得分数为L*X*R
2)如果被打爆的气球的左边有没被打爆的气球,找到离被打爆气球最近的气球,假设分数为L:如果被打爆气球的右边所有气球都已经被打爆,获得分数为L*X。
3)如果被打爆气球的左边所有的气球都已经被打爆:如果被打爆气球的右边有没被打爆的气球,找到离被打爆气球最近的气球。获得分数为X*R.
4)如果被打爆气球的左边和右边所有的气球都已经被打爆。获得分数为X。
目标是打爆所有气球,获得每次打爆的分数。通过选择打爆气球的顺序,可以得到不同的总分,请返回能获得的最大分数。

输入描述:
输出包括两行,第一行包括一个整数n(0<=n<=500),第二行包括n个整数,代表数组arr (1<=arr[i]<=100)。


输出描述:
输出包括一个整数,代表可能获得的最大分数。
示例1

输入

3
3 2 5

输出

50

说明

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

输入

8
23 4 45 65 23 43 54 56

输出

639019

备注:
时间复杂度,空间复杂度
0这个用例是真的可耻啊,0没意义也就算了,你连第二行都不输入,那输入格式都不统一了。这个题是一个范围上的尝试模型,为了编程上的方便,我们对气球数组的首尾补充两个1,防止打爆端点气球时没有邻居分数可以乘从而导致边界问题。假设有一个函数f(L,R)表示将L~R上的所有气球都打爆的最大得分,很容易可以想到两个尝试方向:
  1. L~R的范围上,尝试先打爆位置i的气球,此时打爆它会乘走i-1i+1的分值,如果下一次去打i+1位置的气球,此时i位置的气球已经不在了,可我们在决定打i+1位置的气球时,这件事无从得知。所以会存在某个位置,当我们决定要打爆该位置上的气球时并不能确定它此时的左右邻居是哪个气球,因此无法计算打爆它所带来的收益。
  2. L~R的范围上,尝试最后打爆位置i的气球,既然是最后打爆i位置的气球,那么在此之前肯定我们需要先把L~i-1i+1~R上的气球打完。这样就能够调用f(L,i-1)f(i+1,R),当前i位置气球的分值乘上L-1R+1位置气球的分值+这两个区间上打气球的得分就是f(L,R)
因此我们很容易发现,第二种尝试才能够确定打爆i位置的气球时能够获得的收益(因为知道它的左右两边都是哪个气球)。此问题存在两个变化的端点LR,是个二维动态规划问题,这里提供一个把暴力递归改成记忆化搜索的版本。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        if(n == 0){
            System.out.println(0);
        }else{
            String[] strArr = br.readLine().split(" ");
            int[] arr = new int[n + 2];
            arr[0] = 1;
            arr[arr.length - 1] = 1;
            for(int i = 1; i <= n; i++){
                arr[i] = Integer.parseInt(strArr[i - 1]);
            }
            int[][] memory = new int[arr.length][arr.length];
            System.out.println(burstBalloons(arr, 1, n, memory));
        }
    }
    
    private static int burstBalloons(int[] arr, int left, int right, int[][] memory) {
        if(memory[left][right] > 0) return memory[left][right];
        if(left == right){
            // 区间上只有一个数,直接返回
            return arr[left - 1] * arr[left] * arr[right + 1];
        }
        // 尝试最后打爆所有位置,选择最大的得分
        memory[left][right] = Math.max(arr[left - 1]*arr[left]*arr[right + 1] + burstBalloons(arr, left + 1, right, memory),
                                       arr[left - 1]*arr[right]*arr[right + 1] + burstBalloons(arr, left, right - 1, memory));
        for(int i = left + 1; i < right; i++){
            memory[left][right] = Math.max(memory[left][right], arr[left - 1]*arr[i]*arr[right + 1] + burstBalloons(arr, left, i - 1, memory) + burstBalloons(arr, i + 1, right, memory));
        }
        return memory[left][right];
    }
}

编辑于 2021-12-08 14:01:04 回复(0)

问题信息

上传者:小小
难度:
1条回答 6218浏览

热门推荐

通过挑战的用户

查看代码
打气球的最大分数