zoukankan      html  css  js  c++  java
  • Leetcode 312. [312] 戳气球 动态规划

    /*
     * @lc app=leetcode.cn id=312 lang=cpp
     *
     * [312] 戳气球
     *
     * https://leetcode-cn.com/problems/burst-balloons/description/
     *
     * algorithms
     * Hard (67.72%)
     * Likes:    715
     * Dislikes: 0
     * Total Accepted:    45.1K
     * Total Submissions: 66.6K
     * Testcase Example:  '[3,1,5,8]'
     *
     * 有 n 个气球,编号为0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
     * 
     * 现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i
     * - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。
     * 
     * 求所能获得硬币的最大数量。
     * 
     * 
     * 示例 1:
     * 
     * 
     * 输入:nums = [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:
     * 
     * 
     * 输入:nums = [1,5]
     * 输出:10
     * 
     * 
     * 
     * 
     * 提示:
     * 
     * 
     * n == nums.length
     * 1 
     * 0 
     * 
     * 
     */

    思路:

    labuladong

    难点在于定于dp数组以及如何进行选择

    这里需要对问题进行一个简单地转化。题目说可以认为nums[-1] = nums[n] = 1,那么我们先直接把这两个边界加进去

    那么我们可以改变问题:在一排气球points中,请你戳破气球0和气球n+1之间的所有气球(不包括0n+1),使得最终只剩下气球0和气球n+1两个气球,最多能够得到多少分

    现在可以定义dp数组的含义:

    dp[i][j] = x表示,戳破气球i和气球j之间(开区间,不包括ij)的所有气球,可以获得的最高分数为x

    那么根据这个定义,题目要求的结果就是dp[0][n+1]的值,而 base case 就是dp[i][j] = 0,其中0 <= i <= n+1, j <= i+1,因为这种情况下,开区间(i, j)中间根本没有气球可以戳。

    ij就是两个「状态」,最后戳破的那个气球k就是「选择」。

    根据刚才对dp数组的定义,如果最后一个戳破气球kdp[i][j]的值应该为

    dp[i][j] = dp[i][k] + dp[k][j] 
             + points[i]*points[k]*points[j]

    你不是要最后戳破气球k吗?那得先把开区间(i, k)的气球都戳破,再把开区间(k, j)的气球都戳破;最后剩下的气球k,相邻的就是气球i和气球j,这时候戳破k的话得到的分数就是points[i]*points[k]*points[j]

    那么戳破开区间(i, k)和开区间(k, j)的气球最多能得到的分数是多少呢?嘿嘿,就是dp[i][k]dp[k][j],这恰好就是我们对dp数组的定义嘛!

    由于是开区间,dp[i][k]dp[k][j]不会影响气球k;而戳破气球k时,旁边相邻的就是气球i和气球j了,最后还会剩下气球i和气球j,这也恰好满足了dp数组开区间的定义。

    那么,对于一组给定的ij,我们只要穷举i < k < j的所有气球k,选择得分最高的作为dp[i][j]的值即可,这也就是状态转移方程:

    // 最后戳破的气球是哪个?
    for (int k = i + 1; k < j; k++) {
        // 择优做选择,使得 dp[i][j] 最大
        dp[i][j] = Math.max(
            dp[i][j], 
            dp[i][k] + dp[k][j] + points[i]*points[j]*points[k]
        );
    }

    对于任一dp[i][j],我们希望所有dp[i][k]dp[k][j]已经被计算,画在图上就是这种情况:

     

    class Solution {
    public:
        int maxCoins(vector<int>& nums) {
            int n=nums.size();
            nums.insert(nums.begin(),1);
            nums.push_back(1);
            vector<vector<int>> dp(n+2,vector<int>(n+2,0));
            for(int i=n;i>=0;--i){
                for(int j=i+1;j<n+2;++j){
                    for(int k=i+1;k<j;++k){
                        int temp=dp[i][k]+nums[i]*nums[k]*nums[j]+dp[k][j];
                        dp[i][j]=max(dp[i][j],temp);
                    }
                }
            }
            return dp[0][n+1];
        }
    };
    联系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=
  • 相关阅读:
    一款好用的绘图软件gnuplot
    剑指offer--18.从尾到头打印链表
    剑指offer--17.第一个只出现一次的字符
    剑指offer--16.数组中重复的数字
    剑指offer--15.把字符串转换成整数
    剑指offer--14.求1+2+3+...+n
    剑指offer--13.二进制中1的个数
    剑指offer--12.不用加减乘除做加法
    剑指offer--11.数组中出现次数超过一半的数字
    剑指offer--10.最小的K个数
  • 原文地址:https://www.cnblogs.com/zl1991/p/14736183.html
Copyright © 2011-2022 走看看