zoukankan      html  css  js  c++  java
  • 戳气球 动态规划

    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!种情况,这属于组合问题。

    参考:

  • 相关阅读:
    android面试(4)---文件存储
    android面试(3)---基本问题
    android面试(2)----组件
    android面试(1)----布局
    Android四大组件之BroadCast
    Android四大组件之Service(续2)
    Android四大组件之Service(续)
    Android四大组件之Service
    Android四大组件之Activity & Fragement(续)
    172. Factorial Trailing Zeroes(阶乘中0的个数 数学题)
  • 原文地址:https://www.cnblogs.com/gzhjj/p/14123378.html
Copyright © 2011-2022 走看看