leetcode-312

来源:互联网 发布:最好看的韩国r级 知乎 编辑:程序博客网 时间:2024/05/17 13:39

Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent.
Find the maximum coins you can collect by bursting the balloons wisely.

Note:
(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Example:

Given [3, 1, 5, 8]

Return 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

原数组 [3,1,5,8]
解法一:

将问题分解成一个或多个子问题,假设第i个气球是第一个扎破的气球,我们可以得到 nums[i - 1] * nums[i] * nums[i + 1] + maxCoins(int[] newNums) 个硬币,其中newNums表示的是删掉 nums[i] 后由数组nums中剩余元素构成的新数组。

1.创建新数组用于计算
将原数组前后各加1 改变后 [1,3,1,5,8,1] (这样的好处是 ) 计算时总能满足
score[i]=nums[i-1]*nums[i]*nums[i+1]. 所以开始时从下标1开始,到下标 n-1结束;

2.根据公式
SumScore[i]=nums[i-1]*nums[i]*nums[i+1]+余下最大{减少后的数组,数组的Score最大值};

3.余下递归计算

根据题意假设取出第i个数 MaxCoins

import java.util.Scanner;public class MaxCoins {    public static void main(String[] args) {        Scanner in = new Scanner(System.in);        int res;        int _score_size = 0;        _score_size = Integer.parseInt(in.nextLine().trim());        int[] _score = new int[_score_size];        int _score_item;        for(int _score_i = 0; _score_i < _score_size; _score_i++) {            _score_item = Integer.parseInt(in.nextLine().trim());            _score[_score_i] = _score_item;        }        res = getMaxCoins(_score);        System.out.println(String.valueOf(res));    }    private  static  int[] getArray(int i,int[] array){        int[] newArray=new int[array.length-1];        for (int j = 0,y=0; j < array.length; j++) {            if (j!=i){                newArray[y]=array[j];                y++;            }        }        return newArray;    }    private  static int getMax(int i,int[] array){        if (array.length>3){        int[] newArray=getArray(i,array);        int[] storageScore=new int[newArray.length];        for (int j = 1; j <=newArray.length-2; j++) {            storageScore[j]=newArray[j-1]*newArray[j]*newArray[j+1]+getMax(j,newArray);        }        int Max=0;        //遍历取出最大        for (int j = 1; j < storageScore.length; j++) {            if (Max<storageScore[j]){                Max=storageScore[j];            }        }        return Max;        }else if(array.length==2) {            return array[1];        }        return 0;    }    private static int getMaxCoins(int[] score) {        int[]  Nums=new int[score.length+2];        int n=1;        for (int i:             score) {            Nums[n++]=i;        }        Nums[0]=Nums[n++]=1;        int[] storageScore=new int[score.length+1];        for (int i = 1; i <=n-2; i++) {            //取出一个数后,将其余的数变成一个数组。也就是枚举法            storageScore[i]=Nums[i-1]*Nums[i]*Nums[i+1]+getMax(i,Nums);        }        //定义Max        int Max=0;        //遍历取出最大        for (int i = 1; i < storageScore.length; i++) {            if (Max<storageScore[i]){                Max=storageScore[i];            }        }        return Max;    }}

结果:
23 / 70 test cases passed.

[7,9,8,0,7,1,3,5,5,2] : Time Limit Exceeded

解法二:

dp方法

dp[left][right]的意义是left和right区间内的得分值

dp[left][right]=dp[left][i]+当前+dp[i][right] 才是关键

所以do[0][n-1]为最终结果

import java.util.Scanner;public class Main {    public static void main(String[] args){        Scanner in = new Scanner(System.in);        int res;        int num = 0;        //输入靶的个数        num = Integer.parseInt(in.nextLine().trim());        int[] nums = new int[num];        int _num_item;        for(int _num_i = 0; _num_i < num; _num_i++) {            //依次输入每个靶的得分,            _num_item = Integer.parseInt(in.nextLine().trim());            nums[_num_i] = _num_item;        }        res = maxCoins(nums);        System.out.println(String.valueOf(res));    }        public static  int maxCoins(int[] nums) {        //创建新数组   前后加一    start        int[] newNums=new int[nums.length+2];        int n=1;        for (int i:nums) newNums[n++]=i;        newNums[0]=newNums[n++]=1;        //创建新数组   前后加一    end            // 创建dp记录n*n情况        int[][] dp = new int[n][n];            //从新数组的第一个下标取,最多取到坐标为newNums.length-2         //k为 left到right之间消失了几个   范围是1(没有消失的)            //最多消失应该是原数组长度  nums.length=newNums.length-2            for (int k = 0; k <n-2; k++) {                //left 为扎破第i个气球的左边  left的范围是  newNums的第一个数到  newNums.length-3                for (int left = 0; left <=newNums.length-3-k; left++) {                    //right为扎破第i个气球的右边   右边的范围是 left+k+2  加入没有消失  right-left=2                    int right = left+k+2;                    //所以扎破的i 的开始位置是  left+1  结束位置  是right-1                    for (int i = left+1; i <=right-1; i++) {                        //本身   扎破第i个的分数  nums[i-1]*nums[i]*nums[i+1]                        //但是可能会有消失的气球  所以此时用left和right代替   所以  扎破第i个气球   newNums[left]*newNums[i]*newNums[right]                        int score=newNums[left] * newNums[i] * newNums[right];                        //用dp来记录当  left和right 中间的气球被扎破时最高得分是多少                        //判断最新扎破是否比原来分数高                        //dp[left][right]= dp[left][i]+扎破当前得分+dp[i][right]                        dp[left][right] = Math.max(dp[left][right],   dp[left][i]+score + dp[i][right]);                    }                }            }            //dp[left][right]  意为当left到right中间的气球被扎破时的最大得分         //dp[0][n-1]位原问题的解        return dp[0][n - 1];        }}

版本三:简洁版

import java.util.Scanner;public class Solution {    public static void main(String[] args) {        Scanner in = new Scanner(System.in);        int res;        int _score_size = 0;        //输入靶的个数        _score_size = Integer.parseInt(in.nextLine().trim());        int[] _score = new int[_score_size];        int _score_item;        for(int _score_i = 0; _score_i < _score_size; _score_i++) {            //依次输入每个靶的得分,            _score_item = Integer.parseInt(in.nextLine().trim());            _score[_score_i] = _score_item;        }        res = getmaxCoins(_score);        System.out.println(String.valueOf(res));    }    private static int getmaxCoins(int[] num) {        int[] newNum=new int[num.length+2];        int n=1;        for (int i:             num) {            newNum[n++]=i;        }        newNum[0]=newNum[n++]=1;        int[][] dp=new int[n][n];        for (int disappear  = 2; disappear <n ; disappear++) {            for (int left = 0; left < n - disappear; left++) {                int right=left+disappear;                for (int i = left+1; i < right; i++) {                    //如何知道扎破顺序                    dp[left][right]=Math.max(dp[left][right],dp[left][i]+newNum[left]*newNum[i]*newNum[right]+dp[i][right]);                }            }        }        return dp[0][n-1];    }}

版本四:求出扎破顺序?

回溯算法

写在最后

最终是要讲一个大问题分解成几个具有相同的小问题

dp[left][right]的意义是第left和第right区间内的得分值

dp[left][right]=dp[left][i]+当前+dp[i][right] 才是关键

所以通过从后往前找先找出只扎破一个,再找出扎破两个…直到扎完,left=0,right=n-1的情况才算完成

dp[left][right]=dp[left][i]+当前+dp[i][right] 才是关键dp[left][right]=dp[left][i]+当前+dp[i][right] 才是关键dp[left][right]=dp[left][i]+当前+dp[i][right] 才是关键

参考

LeetCode312

原作者题解