LeetCode (Li ボタン) 312 の質問: 風船をつつく ---- 詳細なメモ付きの動的プログラミング ソリューション

问题描述

0 から n-1 までの番号が付けられた n 個のバルーンがあり、各バルーンには番号が付けられており、その番号は配列 nums に存在します。
すべての風船を割るよう求められます。i 番目の風船を割ると、nums[i-1] * nums[i] * nums[i+1] コインを獲得できます。ここで、i - 1 と i + 1 は、i に隣接する 2 つのバルーンのシリアル番号を表します。i - 1 または i + 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

输入:nums = [1,5]
输出:10

思路分析

この種の問題を解決する最も価値のある問題の場合、動的計画法ソリューションが第一の選択肢です。動的計画法 yyds、はははは。動的計画法で解ける問題についても触れましたが、動的計画法で問題が解けるかどうかを判断するには、まず問題が最適な部分問題構造を持っているかどうかを確認する必要があります。最適な部分問題構造を見つけることは、実は DP を定義することに相当します。配列。この問題では、部分問題を i から j までの番号が付けられた風船によって取得できるコインの最大数として定義でき、DP 配列は 2 次元配列 DP[ i ][ j ] として定義されます。
この問題では、風船を割ると両側の風船からコイン報酬を獲得できますが、風船の数が多すぎると、どれを先に突けばより多くのコイン報酬を獲得できるかわかりません。風船は割られ、位置 k に風船が 1 つだけ残っています。
位置 k にある最後の風船が最も多くのコイン報酬を獲得すると仮定します。つまり、i から j までの番号が付けられた風船で得られるコイン報酬の合計数は、DP[ i ][ j ] = DP[ i ][ k ] + nums[ i ] * nums[ k ] * nums[ j ] で、実際には次のようになります。ガバナンスのスコアと同様に、k の左側と右側は別々に計算されますi と j の間には非常に多くの風船があるのですが、最後に風船を割ったときに、どれが最も多くのコインを獲得できるかをどうやって知ることができるのでしょうか? 実際、すべてのケースをリストし、最大値を取得します。
(i, j) の開いた区間に数値が 3 つだけあるときに計算を開始し、それぞれの小さな区間に金貨の最大値を格納し、その後、小さな区間ですでに計算された数値を使用して、徐々に大きな区間に拡張します。より大きな間隔を計算するだけで十分です (これは動的転送の利点でもあり、後者の結果は前者の結果の影響を受け、前者の結果を通じて計算できるため、計算の繰り返しが回避されます)。

代码

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        # 先给nums数组扩展边界
        nums.insert(0, 1)
        nums.insert(len(nums), 1)
        # 创建DP数组
        DP = [[0] * len(nums) for i in range(len(nums))]

        # 寻找最后戳破哪一个气球得到的硬币奖励最多
        def select_last_ball(i, j):
            # 定义(i, j)区间获得的硬币数
            ij_coin = 0
            # 因为是(i, j)开区间,所以从 i + 1 开始, j - 1结束
            for k in range(i + 1, j):
                # 除K之外, 左右两边获取的硬币奖励
                left = DP[i][k]
                right = DP[k][j]
                # (i, j)区间气球获得硬币奖励为左边区间气球 + 右边区间气球 + 第K个气球(最后扎破), 戳破第K个气球是,就只剩[i, j]区间就只剩第i, j, k 3个气球了
                temp = left + nums[i]*nums[k]*nums[j] + right
                # 比较然后取最大值
                if temp > ij_coin:
                    ij_coin = temp
            DP[i][j] = ij_coin

        #对每一个区间长度进行循环, n为[i, j]区间的长度
        for n in range(2,len(nums)): #因为区间内至少要有三个气球, 所以从最小长度3开始,所以n从2开始
            #开区间长度会从3一直到len(nums)
            #因为这里取的是range,所以最后一个数字是len(nums)-1
            #对于每一个区间长度,循环区间开头的i
            for i in range(0,len(nums)-n): #i+n = len(nums)-1

                #计算这个区间的最多金币
                select_last_ball(i,i+n)
        # 返回编号区间为[0, len(nums)]内的气球, 即题目所给的所有气球
        return DP[0][len(nums)-1]      

运行结果
ここに画像の説明を挿入
总结

この問題は非常に難しいと個人的に感じています。動的計画法と分割統治を組み合わせたものです。その DP 配列の定義は見つけるのが難しく (個人的には と感じています)、考察も困難です。すべての風船に穴が開いたと仮定すると、残りの境界線は 2 つだけですが、風船と真ん中の風船というアイデアがこの問題を解決する鍵となります。連絡してください~ 一緒に学び、コミュニケーションをとるために皆さんを歓迎します。

参考

https://leetcode-cn.com/problems/burst-balloons/solution/zhe-ge-cai-pu-zi-ji-zai-jia-ye-neng-zuo-guan-jian-/

おすすめ

転載: blog.csdn.net/Just_do_myself/article/details/118383554