leetcode312 重新思考动态规划

问题:

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.


解法1:枚举

n个气球共n!个全排列

解法2:动态规划

动态规划的本质是用易解的小规模问题,组合还原大规模问题。缩小与还原是互逆的过程,知道了如何缩小,就知道了如何还原。在之前的文章里我认为表格里列举的是可能性,做完这题,我认为dp表格里列出的是一个个小规模问题,全部为组合还原大规模问题服务。

缩小规模过程中,从n规模缩小到n-1规模,比如考虑k号气球,最直观的想法是先戳k,再戳剩下的不连续的气球区间,这样就完成了规模的缩小,但是不连续的区间用下标表示非常麻烦,那么如何用连续的区间表示这个不连续的区间?在间断点k处切为两半,分为三部分(1~k-1)(k)(k+1~n),这样左右两边都连续了,可以用二维数组dp[i][j]表示从i到j。

还原规模,三个部分怎样才能组合成原来的问题?左右两边地位是等价的,可以同时戳,并且要求互不干扰,而互不干扰的前提是区间边界稳定不变,那么对k的要求就是k必须在最后戳,只有这样才能还原规模。

综合以上两步的思考,最终的结论是,以先戳两边,最后戳中间的方式缩小/还原规模。

令气球编号从1到n,边界nums[0] = nums[n+1] = 1

  • 选择状态
    dp[i][j]表示戳编号在开区间(i,j)中的气球,能得到的最大价值。
    选择开区间的原因是这样体现了边界。
  • 状态转移方程
    dp[i][j] = max{ dp[i][k]+dp[k][j]+nums[i]*nums[k]*nums[j] },i<k<j
  • 边界
    dp[i][i+1] = 0
  • 处理顺序
i / j 0 1 2 3 n+1
0 - 0
1 - - 0
2 - - - 0
- - - -
n - - - - - 0

状态转移方程中涉及到的状态在该点左方,或下方,因此处理顺序应为从左上到右下,直到dp[0][n+1]

class Solution {
    
    
public:
    int maxCoins(vector<int>& nums) {
    
    
        int n = nums.size();
        nums.emplace(nums.begin(), 1);
        nums.emplace_back(1);
        int dp[n+1][n+2] = {
    
    };
        for(int l=2; l<n+2; l++){
    
    
            for(int i=0; i<n; i++){
    
    
                int j = i + l;
                if(j<n+2){
    
    
                    for(int k=i+1; k<j; k++){
    
    
                        int money = dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j];
                        if(money>dp[i][j]) dp[i][j] = money;
                    }
                }
            }
        }
        return dp[0][n+1];
    }
};

猜你喜欢

转载自blog.csdn.net/sinat_37517996/article/details/104733350