[概要]動的計画

ダイナミックプログラミング(動的計画法)が一般的に使用される最適化問題を解決するために(最適化問題)、それはサブの重複の問題は、それが独立したサブ問題、異なるサブサブサブの問題ではないような場合に適用されます(一般的な問題を抱えていることは、サブの問題ですサブ問題)。これは、分割統治法が異なる明らかで分割し、重複しないサブ問題に問題を克服し、これらのサブ問題は解決され、これらの問題は最終的に最終的な溶液を得マージします。
共通のサブ問題が発生した場合には、分割統治が、それは同じサブサブ問題に何回も解決される、不必要な多くの作業を行います。ダイナミックプログラミングは同じではありませんが、それだけで、計算の不必要な重複を避けるために、テーブルに保存し、各サブサブ問題のために一度に解決されます。
動的プログラミングの使用は、ここでは、心に留めておくと、この問題(最適解)への最適解を模索することである(最適解)を解決するだけで最適なソリューションであることがより最適解があるかもしれないからです。
DPの問題は、最適化理論となした後、効果を満たす必要があります適用されます。
1. 最適化の原則:最適解はサブ最適解が問題の解決であるが含まれている場合、問題は、最適化原理を満たす最も基礎構造を持っているといわれています。(すなわち、最高のを選択することにより、特定の最適な下部後)
2. ノー後の効果:進化のプロセスは、もはや簡単に言えば、以前のさまざまな状態の決定や、影響を受けませんその後、ステージの状況、一度、唯一の現在の状態によって、プロセスの将来の発展に影響を与えるために、現在の状態は、歴史の完全な要約の後で、以前の歴史「未来は過去とは何の関係もありません」です。
3 重複副問題:それは、各サブ問題に独立していない、意思決定の次の段階で問題の子供がに複数回使用することができます。(このプロパティは、動的プログラミング必要条件が適用されないが、これは動的プログラミングアルゴリズムの性質でない場合、他のアルゴリズムと比較して利点を有していません)。実際には、DPは、本質的に時間のアプローチのための空間、時間の複雑さを低減するために、無サブ反復計算の問題、プロセスに格納されなければならない様々な状態です。

ダイナミックプログラミングは次第に暴力から列挙を最適化することができます。問題解決の様々な方法:暴力列挙 - >再帰 - >メモアルゴリズム - >正規化アルゴリズムを動かします

[leetcode] 10.Regularの表現のマッチング

Pビット列、文字、ポイントのための\(\) 文字\(* \)四つの要素、ポイントは、アルファベットの任意の文字に一致する、同じ、点マッチング文字\は(* \)であってもよい(任意の文字にマッチします任意の異なる文字、例えば、\(* \) ABCと一致する)、文字\(* \)任意の連続した同一の文字に一致する、ことは注目に値する、\は(* \) 任意のゼロを含みます。ので\(* \)は、任意の数を一致させることができ、引き起こされるかどうかをテストSおよびPの正確な一致を正確に決定することは困難である場合\(* \)適切な文字に一致するが、これはこの問題の重要な点です。

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        # dynamic programming
        len_s = len(s)
        len_p = len(p)
        # initialize
        dp = [[False for i in range (len_p+1)] for j in range(len_s+1)]
        dp[0][0] = True
        # initialize dp[0][j]
        for j in range(1,len_p):
            if p[j] == '*':
                dp[0][j+1] = dp [0][j-1]
        for i in range(1,len_s+1):
            for j in range(1,len_p+1):
                if s[i-1] == p[j-1] or p[j-1] == '.':
                    dp[i][j] = dp[i-1][j-1]
                elif p[j-1] == '*':
                    dp[i][j] = (dp[i-1][j-2] and (p[j-2] == s[i-1] or p[j-2] == '.')) or dp[i][j-2] or
                               (dp[i-1][j] and (s[i-1] == p[j-2] or p[j-2] =='.'))
        return dp[len_s][len_p]

他の類似のトピックがあります:[leetcode] 44.Wildcardマッチング。

【leetcode] 62.Unique経路

キーは、動的プログラミング漸化式を取得することです。この問題のために、番号が経路の特定の点は、その左側にポイントに到達するパスの数とパス数と等しい達します。すなわち、以降の点(i、j)はパスの総数:方法[I] [j]は=出発点(I、J-1)の数である:方法[I]、[J-1] +出発点( I-1、j)の合計:方法[I-1] [J]。$方法[I] [J] =の方法[I] [J-1] +方法[I-1]〜[J] $:だから我々は再帰を取得します

class Solution:
    def uniquePaths(self, m, n):
        paths = [[1 for i in range(n)] for i in range(m)]
        for i in range(1,m):
            for j in range(1,n):
                paths[i][j] = paths[i-1][j]+paths[i][j-1]
        return paths[m - 1][n - 1]

[leetcode] 64.Minimumパスの合計; [leetcode] 63.UniqueパスII:他の類似のトピックがあります。

[leetcode] 72.Edit距離

編集距離も非常に重要と古典的な問題です。
このアルゴリズムは、Sである、[1 ... I]を[1 ... J(例えば、座って子猫に変換)に必要な最小数(即ちいわゆる編集距離)動作をtに変換され、オペランドをdに格納されています[I、J]における(Dは、上に示した二次元配列で表現されます)。

  • 1行1列目にこれはよく理解されている例えば、我々は子猫が空の文字列に変換され、確かに正しい、我々は長子猫のオペレータに必要な演算の数は、(子猫は、すべての文字に対して行われます廃棄されました)。
  • 私たちは、3つの文字は操作であってもよくあります。
    • 我々はk個のオペランドSを使用することができた場合は、[1 ... i]は、[1 ... J-1] Tに変換され、我々は唯一の[J]をtに必要なSの端面に追加することができます[1 ... i]はtをに変換され、 [1 ... j]は、K + 1つのオペランド
    • 我々はk個のオペランドsを使用することができた場合は、[1 ... I-1]は、最後の完全な変換、オペランドのk + 1から削除することができ、我々は唯一のS [i]にする必要があり、[1 ... j]をtに変換され、
    • 我々はsのk個のオペランドを使用できる場合は、[1 ... I-1]を[1 ... J-1] Tに変換され、我々はSに(S [i]は!= T [J])必要な場合にのみ必要T [J]で置換されている[i]は、K +コストのための操作の必要な数(S [I] == T [j]の場合かどうか、変換のコストを表すが、コストがそうでなければ1、0です)。
  • S [1 ... n]はもちろん[1 ... M] Tに変換され、すべてのすべてのtについてだ、それは、D [N、M](表の右側)に変換する必要があり、我々は必要な結果です。
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m,n = len(word1),len(word2)
        dp = [[0 for j in range(n+1)] for i in range(m+1)]

        for i in range(m+1):
            for j in range(n+1):
                if i==j==0:
                    dp[0][0] = 0
                elif i==0:
                    dp[0][j] = j
                elif j==0:
                    dp[i][0] = i
                else:
                    if word1[i-1] == word2[j-1]:
                        dp[i][j] = dp[i-1][j-1]
                    else:
                        dp[i][j] = 1+min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])
        return dp[-1][-1]
[leetcode] 91.Decode方法

この質問は、必要とするので、DPとして明らかに、唯一、リストされている各メソッドに要求することなく、そこにあるどのように多くの方法デコードします。現在の要素がXY、ieXYZとして彼の前にZ、2であると仮定する。
1. Z = 0の場合、及びY = 1又は2です。その後、Zは、XのDP値に対応する実施例10のための唯一の説明、DP [i]はDP [I =ので、DPに等しい -2]です。
2. YZ [11、19]又はを配置した場合[21、26]の双方の範囲で、次いで明確我々はこの方法を復号するために2つの2桁を有する、すなわち、DP [I] = DP [I -1 ] + DPは、[I-2] 、これはDP [I] = DP [iがないことに留意されたい -1] +1。最後の例は21212です。
3. Xは、例えばYZ = 81、0でない場合。そして、DP [I] = DP [I -1]。
最後に、我々はDPに2を取らなければならないので、前に1を加えたときに[I-2]、私たちはDPのリストを初期ことに注意してください。このため、プラス1一流の。だから、DP [i]は、S [I-1]に対応している場合。YZは、[(I-1)-1対応するSである :(I-1)+1]。

class Solution:
    def numDecodings(self, s: str) -> int:
        if not s or s[0] == '0':
            return 0
        dp = [1 for i in range(len(s)+1)]
        for i in range(2,len(s)+1):
            nums = s[i-2:i]
            if 1<int(nums)<10 :
                dp[i] = dp[i-1]
            elif 11<=int(nums)<=19 or 21<=int(nums)<=26:
                dp[i] = dp[i-1]+dp[i-2]
            elif int(nums) == 10 or int(nums) == 20:
                dp[i] = dp[i-2]
            elif nums[-1]=='0':
                dp[i] = 0
            else:
                dp[i] = dp[i-1]
        return dp[-1]
【leetcode] 97.Interleavingストリング

これは、再帰のいずれかにマッチしたS1またはS2のそれぞれに、再帰的に行うことができます。しかし、タイムアウト。
だから、動的計画法を使用することを検討してください。
S1、S2の2つだけの文字列、左上隅から右下隅が行ったかどうかを判断するための二次元マップに平坦化することができます。
i番目の素子S1、S2 j番目の要素に到達する到達場合:
右のステップは、マップS2にある[J-1]マッチS3を[Iがj-1 +] 。
次のステップはS1 [I-1]マッチS3をマッピングすることである[Iは、J-1 +] 。

class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        if not s1:return s2==s3
        if not s2:return s1==s3
        m,n = len(s1),len(s2)
        if m+n != len(s3):return False
        dp = [[False for j in range(n+1)] for i in range(m+1)]
        for i in range(m+1):
            for j in range(n+1):
                if i==j==0:
                    dp[i][j] = True
                elif i == 0:
                    dp[0][j] = (dp[0][j-1] and s2[j-1] == s3[j-1])
                elif j == 0:
                    dp[i][0] = (dp[i-1][0] and s1[i-1] == s3[i-1])
                else:
                    dp[i][j] = (dp[i-1][j] and s1[i-1] == s3[i+j-1]) or (dp[i][j-1] and s2[j-1] == s3[i+j-1])
        return dp[m][n]
【leetcode] 115.Distinctサブ配列

相互に異なるシーケンス。SによるT DP前番号をi番目とj番目のラインの前に[i] [j]は記録され、その後、最終的な目標は、DP [S.size()] [T.sizeである ()];
初期、J = 0時間、DP [I] [0] = 1、 なぜならそれらすべてすべてになるのヌル文字を削除し、1つだけであることもできます。
次の再帰式:
iおよびjは1から開始され、少なくとも一つの試合が始まるの長さは、jが私は意味よりも大きいのでjは、Iより大きくありません

  • 場合\(I == jは\)は、次にDP [I] [J] = S.substr(0、I)== T.substr(0、j)は、
  • もし\(I!= J \) 2例で
    • !S [I-1] = T [J-1]、影響が付加されていないが、私は同じで添加しDP [i] [j]はDPを= [I - 1]〜[J]。
    • S [I-1] ==あるいはその後[J-1] Tの時間、現在の文字一致または不一致、DP [I] [j]はDPを= [I - 1] [J-1] + DP [ I - 1] [J]。
    class Solution:
      def numDistinct(self, s: str, t: str) -> int:
          m,n = len(s),len(t)
          dp = [[0 for j in range(m+1)] for i in range(n+1)]
          dp[0][0] = 1
          for  j in range(1,m+1):
              dp[0][j] = 1
          for i in range(1,n+1):
              for j in range(1,m+1):
                  if t[i-1] == s[j-1]:
                      dp[i][j] = dp[i-1][j-1]+dp[i][j-1]
                  else:
                      dp[i][j] = dp[i][j-1]
          return dp[n][m]
証券IIIを購入し、販売する[leetcode] 123.Best時間

この道路は、非常に単純なアルゴリズムをIIアイデアは非常に簡潔です証券を売買するために購入し、株式・ベストタイムを販売するために、2行のベストタイムの前で、最も困難かつ最も複雑なものでは一連の問題の株式を購入するための最良の時間です。この道路は、ほとんどの取引で最大の利益を見つけるために、二回必要で、または解決するために、動的プログラミング、動的プログラミングを使用する必要があり、ここでは2つの変数がローカルとグローバルで更新するために、2つの再帰式を必要とする。実際には、我々は、少なくともk回を見つけることができます取引利益の最大の後、一般的な解決策をk = 2を設定することができます、この質問への答えです。私たちは、地元の定義[j]が販売する最後の日の最初の日、私のj回最後の売却取引と最大の利益にまで達している[i]が、これは局所最適です。その後、我々は最高の利益jのトランザクションまでのi日に到着するグローバル[i]の[j]を定義し、これはグローバル最適です。彼ら漸化式:
ローカル[I] [J] = MAX(グローバル- [J - 1] + MAX(差分、0)、[I - 1]ローカル+ [J]のdiff [I 1。])
グローバル[ I] [J] = MAX(ローカル[I]、[J]、グローバル[I - 1] [J])
ローカル最適値が比較的小さく、トランザクション前日がグローバルに最適との間の差よりも大きいプラス0であります次のように、前日プラス大きい方のローカル最適値の差、および最適グローバル最適とは、ローカルおよびグローバルな最適前日比較します。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0
        # 二维数组
        # k = 2
        # local = [[0 for j in range(k+1)] for i in range(len(prices))]
        # glob = [[0 for j in range(k+1)] for i in range(len(prices))]      
        # for i in range(1,len(prices)):
        #     diff = prices[i]-prices[i-1]
        #     for j in range(1,k+1):
        #         local[i][j] = max(local[i-1][j]+diff,glob[i-1][j-1]+diff)
        #         glob[i][j] = max(glob[i-1][j],local[i][j])
        # return glob[-1][-1] 

        # 一维数组
        k = 2
        local = [0 for j in range(k+1)]
        glob = [0 for j in range(k+1)]
        for i in range(1,len(prices)):
            diff = prices[i]-prices[i-1]
            for j in reversed(range(1,k+1)):
                local[j] = max(local[j]+diff,glob[j-1]+diff)
                glob[j] = max(glob[j],local[j])
        return glob[-1]

実際には、それはDPのように要約することができる日を表す2つの状態が、販売することを持っており、利益率は必ずしもその日の販売は行っていません。

【leetcode] 132.PalindromeパーティションII

「パリンドローム」構造でなければならない各部分を分割した後、分割された文字列を入力し、それは分割の最小数を必要とします。明らかに、分割の最小数を打つために、単純なアイデアは、網羅的セグメンテーション状況で、その後回文サブストリング分割方法および時間の最小数を構成することができる分割後見つけます。
文字列の場合は、我々はすべての可能なパーティショニングを考慮する必要があり、この問題は長さnの文字列のため、DPの問題に抽象化することができ、セットDPは、[i] [j]は、j番目の文字に、i番目の文字のかどうかを示しますパリンドローム構成、そうである場合、DP [I] [J] = 1; そうでない場合、DP [I] [J] = 0

class Solution:
    def minCut(self, s: str) -> int:
        n = len(s)
        dp = [(i-1) for i in range(n + 1)]
        for i in range(n + 1):
            for j in range(i):
                tmp = s[j:i]
                if tmp == tmp[::-1]:
                    dp[i] = min(dp[i], dp[j] + 1)
        return dp[n]
[leetcode] 139.Wordブレイク

動的計画。DP [i]は、文字列sを表す[を:i]の部分文字列に分割することができる要件を満たしています。グループ内の文字列を与えられた、とDP [j]が真である:それから、(つまり、文字列sのサブストリングに分割可能な要件を満たす[j]は):S [I j]があれば私たちは、それを見ることができますこの時点で、DP [i]はまた真です。

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        dp = [False for i in range(len(s)+1)]
        dp[0] = True
        for i in range(len(s)+1):
            for k in range(i):
                if dp[k] and s[k:i] in wordDict:
                    dp[i] = True
                    # break
        return dp[len(s)]

ダイナミックプログラミング文字列の詳細、我々は見つけることができます:小型/最大ショート需要/最長の文字列を、最適なソリューションを、私たちは子供の全体的な問題を解くことによって行われ、サブ問題の分析によって分解の問題を解決することができます最適なソリューションを提供します。

[leetcode] 174.Dungeonゲーム

ボトムアップの動的プログラミング
最後の状態は、少なくとも全ての位置の後でなければなりませんどのくらいの血液によって決定された前方に移動させるために、初期状態であるような時間ではそう、残りの血液の少なくとも1の最後の部屋を終了し、そのために使用することができます状態は、以下の位置と左側と小さい状態によって決定されます。したがって、基本的な状態方程式は:DP [I] [J] =分(DP [I + 1] [j]は、DP [I]、[J + 1]) -ダンジョン[I] [J]。

class Solution:
    def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
        m, n = len(dungeon), len(dungeon[0])
        dp = [[0 for j in range(n)]for i in range(m)]

        for i in reversed(range(m)):
            for j in reversed(range(n)):
                if i == m-1 and j == n-1:
                    dp[i][j] = max(0, -dungeon[i][j])
                elif i == m-1:
                    dp[i][j] = max(0, dp[i][j+1] - dungeon[i][j])
                elif j == n-1:
                    dp[i][j] = max(0, dp[i+1][j] - dungeon[i][j])
                else:
                    dp[i][j] = max(0, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j])
                
        return dp[0][0] + 1
【leetcode] 198.House強盗

動的計画DP。本質的に、アレイは、1つ以上の非隣接の数、および許容最大値を除去するために、対応します。

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:return 0
        dp = [0 for i in range(len(nums)+1)]
        for i in range(1,len(nums)+1):
            if i==1:
                dp[1] = nums[0]
            else:
                dp[i] = max(dp[i-1],dp[i-2]+nums[i-1])
        return dp[-1]
【leetcode] 213.House強盗II

その上に、最初と最後の間、この制約の下で、マルチループ条件の問題は、月よりも多くを盗むではないでしょう。だから、2例を盗むために:最初は、最初の部屋は最大値を得ることができ、これらの2つのメソッドを盗むしようとしている、第二盗むない最後の部屋を盗むことではありません。

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:return 0
        elif len(nums)==1:return nums[0]
        elif len(nums)==2:return max(nums[0],nums[1])
        else:
            return max(self._rob(nums[1:]),self._rob(nums[:-1]))
        
    
    def _rob(self,nums):
        if not nums:return 0
        dp = [0 for i in range(len(nums)+1)]
        dp[1] = nums[0]
        for i in range(2,len(nums)+1):
                dp[i] = max(dp[i-1],dp[i-2]+nums[i-1])
        return dp[-1]
[leetcode] 221.Maximal広場

動的プログラミング、[I]、[J](i、j)の中の正方形の最大辺の長さで表されるが、右下隅になるように、時間複雑である(\ O(M * N))を\、空間的複雑です\(O(M * N)\)

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        if not matrix:return 0
        m,n = len(matrix),len(matrix[0])
        dp = [[0 for j in range(n)] for i in range(m)]
        res = 0
        for i in range(m):
            for j in range(n):
                if i == 0:
                    dp[i][j] = int(matrix[i][j])
                elif j == 0:
                    dp[i][j] = int(matrix[i][j])
                else:
                    if matrix[i][j] == '1':
                        dp[i][j] = min(dp[i-1][j- 1],dp[i-1][j],dp[i][j- 1])+1
                    else:
                        dp[i][j] = 0
                res = max(res,dp[i][j])
        return res*res

そのDPに注意してください[I] [J]の条件を更新するには、行列[I] [J] == '1'、DPするように更新された[I-1] [J-1]、DP [I-1]〜[J] 、DP [i]は[J-1] +最小値であります

クールダウンして証券を売買する[leetcode] 309.Best時間

二つ
の状態:1。i日の株価-最初の日で、私は残りの利益=(I-2)営業利益の日を残りできるの株式を購入します。
2。私は株式の総利益=最初の(I-1)+日の残りは株式に現在の株価を購入する販売初日で最大の利益。
この情報は、二つの状態を維持する必要があり、最大の利益が得られ、残りの株式を購入することで、最大の利益は、株式を売却した後に得られた、彼らはお互いの情報に依存しています。
最大の利益を維持する方法の再びさらなる分析。
購入する前に利益を残りの後に購入した場合の購入のために、大きなを購入するかどうかに応じて、その日は、すなわち、状態遷移方程式は、より:
= MAX([-Iを1。]買う、売る、[I-2] [I]を購入します- ;価格[I-1])
販売のため、より大きな利益の可用性を売却する株式を売却するかどうかに応じて、同じ日に、状態遷移方程式が
売り[I] = MAX(マーケットプレイス[I- 1]、[I-1買う ] +価格[I-1])を、
また、問題の意味に基づいて、それは理解しやすい3つの状態を、維持しています。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # if not prices:
        #     return 0
        # N = len(prices)
        # buy = [0 for i in range(N+1)]
        # sell = [0 for i in range(N+1)]
        # buy[1] = -prices[0]
        # for i in range(2,N+1):
        #     buy[i] = max(sell[i-2]-prices[i-1],buy[i-1])
        #     sell[i] = max(sell[i-1],buy[i-1]+prices[i-1])
        # return sell[N]
        
        if not prices:
            return 0
        N = len(prices)
        rest = [0 for i in range(N)]
        buy = [0 for i in range(N)]
        sell = [0 for i in range(N)]
        buy[0] = -prices[0]
        for i in range(1,N):
            rest[i] = max(rest[i-1],sell[i-1])
            buy[i] = max(buy[i-1],rest[i-1]-prices[i])
            sell[i] = max(sell[i-1],buy[i-1]+prices[i])
        return sell[-1]
【leetcode] 312.Burstバルーン

動的プログラミングは、
DP [I] [j]はi乃至jの最大値はこの間隔の間に得られることが可能であり、DPの状態遷移式[I] [J] =設けられている MAX(I <K <J)(DP [I] [ K] + DP [K] [ J] + [I] * [K] * [J])

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        nums = [1] + nums + [1]
        n = len(nums)
        dp = [[0 for j in range(n)] for i in range(n)]
        
        # dp[i][j]代表(i,j)区间内取得的最大值  -->关键在于理解dp内涵
        for left in reversed(range(n)):
            for right in range(left+1,n):
                for i in range(left + 1, right):
                    dp[left][right] = max(dp[left][right],nums[left] * nums[i] * nums[right] +dp[left][i] + dp[i][right])
        return dp[0][-1]
【leetcode] 368.Largest可分サブセット

対象配列の要件ことを意味一次元DPを使用して、DP [i]は0〜Iアレイから最大位置を満たすために、被験者の意味です。最初のi、jで各番号を横断した後、前後から、NUMS [I]割り切れる番号を見つけることができるので、それは割り切れる時間を判断し、[I] <DP [J DPを決定している場合 ] + 1、 すなわち用終了インデックスかどうか私は、配列の長さは最長となります。可変長の場合には、親を使用しながら、[i]はiが割り切れる番号の前に更新することができる、[I] DPを更新する必要があります。また、アレイ全体の最長のサブ配列の長さの統計。
nは対象配列の条件を満たし、そして最後にもう一度繰り返す必要があり、アウト統計出力の親の子どもの配列を使用することができること〜私たちは0内で最も長い間隔のために知っている、それぞれの位置の後、最長サブアレイのために知っています。これは最大のインデックスmx_index N用語であるため、出力は逆の順番ですので。

class Solution:
    def largestDivisibleSubset(self, nums: List[int]) -> List[int]:
        if not nums: return []
        N = len(nums)
        nums.sort()
        dp = [1] * N #LDS
        parent = [0] * N
        mx = 1
        mx_index = -1
        for i in range(N):
            for j in reversed(range(i)):
                if nums[i] % nums[j] == 0 and dp[i] < dp[j] + 1:
                    dp[i] = dp[j] + 1
                    parent[i] = j
                    if dp[i] > mx:
                        mx = dp[i]
                        mx_index = i
        res = []
        for k in range(mx):
            res.append(nums[mx_index])
            mx_index = parent[mx_index]
        return res[::-1]

ここで問題解決能力は、同時に、動的プログラミングで状態を保存し、サイトを復元するために余分なスペースを使用し、あります。

【leetcode] 486.Predict勝者

動的計画
問題1.プレイヤーの直接比較し、要素の値をとるが、要素を取る2人の選手との差の問題を置くことではありません。これは重要なステップである、賢いです。
2.再帰式を求める:MAX(NUMSは、[BEG] - -パーティション(BEG + 1、エンド)、NUMS [終了]パーティション(BEG、エンド+ 1)。。)
再帰アルゴリズムにより構成される3は、再帰的表現であります比較的簡単。しかし、非再帰的なアルゴリズムより大きな困難を構築します。最初の初期値DPを割り当て非再帰的なアルゴリズムについては、これは問題解決で私たちの最初のステップです。この問題では、我々は、任意の開始終了位置と二つの結果の間の差を表すために2つの倍精度配列NUMSアレイを使用します。
当初、我々は唯一の対角線上の値を知っています。DP [i]は[I] = NUMS [I]。 これは、よく理解されています。それは、任意の開始と終了を求めているので、次に、二次元アレイの場合、それは確かに二重ループです。再帰的な表現DP既知の要素と動的なプログラミングによって、我々は、我々のニーズの結果を構築することができます。小規模から大規模な問題解決プロセスへの非再帰的な方法。

class Solution(object):
    def PredictTheWinner(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        n = len(nums)
        dp_f = [[0 for j in range(n)] for i in range(n)]
        dp_g = [[0 for j in range(n)] for i in range(n)]
        for i in reversed(range(n)):
            for j in range(i,n):
                if i==j:
                    dp_f[i][j] = nums[i]
                else:
                    dp_f[i][j] = max(nums[i]+dp_g[i+1][j],
                   nums[j]+dp_g[i][j-1])
                    dp_g[i][j] = min(dp_f[i+1][j],dp_f[i][j-1])       
        return dp_f[0][-1] >= sum(nums)/2
[leetcode] 494.Target和

問題にのみデジタルアレイとプログラム番号と同じ目標値を解くこと、各数値は、正または負の符号(負に等しいプラスマイナスの整数)であってもよいです。従って、元の問題を解決するが、問題のプログラムの数のサブセットと等しいNUMS和(P)に変換され、固定値である目標とSUM(NUMS)ので、ナップザック問題を解くに変換することができます。

class Solution:
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        sum_ = sum(nums)
        target = (sum_+S)/2
        if target!=int(target):
            return 0
        if target>sum(nums):
            return 0
        target = int(target)
        dp = [0 for i in range(target+1)]
        dp[0] =1
        for num in nums:
            for i in reversed(range(num,target+1)):
                dp[i] += dp[i-num]
        return dp[-1]
【leetcode] 516.Longest回文サブシーケンス

レンlenは、行の列配列DPを確立- DP [I] [J]私は〜最長サブストリングパリンドロームの長さからなるj個の添字は〜最後に、我々はDPを返却する必要があるサブ文字列を示し[0] [ LEN-1]の値。
DPアレイ更新まず、ポインタ尾からヘッドトラバーサルにI、ポインタjは後部要素から始まるが、テール・ポインタへ横断された私は〜DP起動電流i及びjは要素と等しい場合[I] [i]の値は、1である指さ説明ストリングのI〜Jパリンドロームの長さに適用することができ、更新DP [I] [j]は= DPを[I + 1] [J-1] + 2; 現在の要素が等しくない場合、次いで、2つの命令jはパリンドローム配列エレメントがDPに寄与しない呼ばI、[I] [j]はDPである[I + 1] [J ] とDP [I] [J-1 ] より大きい値を選択しますそれはすることができます。

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        if not s:
            return 0
        n = len(s)
        dp = [[0 for j in range(n)] for i in range(n)]
        for i in reversed(range(n)):
            for j in range(i,n):
                if i == j:dp[i][j] = 1
                elif s[i] == s[j]:
                    dp[i][j] = dp[i+1][j-1]+2
                else:
                    dp[i][j] = max(dp[i+1][j],dp[i][j-1])
        return dp[0][-1]
境界経路の[leetcode] 576.Out

この質問は、Nステップの合計が移動することができ、サッカー、4つの方向に任意のさらなる移動する時間を、置くためにどこかに、私たちに二次元配列を与える、彼はどのように多くの携帯方法の合計を持っている私たちにできる尋ねましたサッカーは結果が膨大な数の可能性があるため、国境を削除するので、私たちは多くのより多くを見てみましょう。だから我々は、多数の再帰的なソリューションスタックすることにより、この結果が破裂しやすいので、DPの使用を検討するために最善の解決策場合ことを知っています。その後、我々は、DP [K] [I] [j]は境界(i、j)の位置からパスの総数のうち、総ダウンk個のステップを表すDPの三次元アレイを、使用します。その後、我々は、[K] [I] [j]は、パスの境界kを歩くの総数のうち、段差K-1を総個数とパスの境界のうち、ステップ4箇所の周りに行くに等しいDPのための再帰式を来あなたは場所の周囲に境界線を持っている場合は、それぞれの位置が範囲外にパスのステップの任意の数を取るためにダウン全体の更新は、我々は数を描くことができるように、そして、直接1を追加、または値DPの配列を見つけるために最後に、単にDP [N] [i] [j]が所望の結果で返します。

class Solution:
    def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int:
        dp = [[0] * n for _ in range(m)]
        for s in range(1, N + 1):
            curStatus = [[0] * n for _ in range(m)]
            for x in range(m):
                for y in range(n):
                    v1 = 1 if x == 0 else dp[x - 1][y]
                    v2 = 1 if x == m - 1 else dp[x + 1][y]
                    v3 = 1 if y == 0 else dp[x][y - 1]
                    v4 = 1 if y == n - 1 else dp[x][y + 1]
                    curStatus[x][y] = (v1 + v2 + v3 + v4) % (10**9 + 7)
            dp = curStatus
        return dp[i][j]

ナップザック問題

[leetcode] 322.Coin変更

動的プログラミングを使用して、金種数が0から必要最小限のコインにできる量+ 1であることを意味長DP量+ 1のアレイを構築する必要があります。

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        dp = [float('inf') for i in range(amount + 1)]
        dp[0] = 0
        for coin in coins:
            for i in range(coin, amount + 1):
                # if dp[i - coin] != float('inf'):
                dp[i] = min(dp[i], dp[i - coin] + 1)
        return dp[amount] if dp[amount]!= float('inf') else -1
【leetcode] 474.Onesとゼロ

この質問は、数は対象媒体の難しさのために、所定の値を超えないことを確実にするために、文字列と文字列の0と1のセットから可能な限り選択されています。
タイトルと0-1ナップザック問題が類似しているが、ここでの違いは、0-1ナップザック問題の総重量を制限しながら、番号0および1に制限することで、動的プログラミングは、古典的なタイトルとみなされます。
数が0より大きいJ、K 1までではない前に、私は、文字列内の文字列の数を表し、DPによって選択された数を超えない[I] [J] [ K]。i番目の統計列番号0および1にそれぞれとCNT0、CNT1、i番目の列は、DPが取られている場合、[I] [J] [ k]はDPを= [I-1]〜[J-CNT0] [K -cnt1] + 1、i番目の列を取らない場合はDPである[I] [J] [ k]はDP [I-1]〜[J] [K] = 2 DPなどの大きい方を[I] [値j] [k]があります。DPので[I] [J] [ [*] [*] [I-1] K] DP それは再利用することができる場合にのみ関連して\(M×n個\)空間的複雑度低減データO(\を(N-Mの*)\)トラバースするとき、ちょうどバックからフロントには横断することができます。特定のコード:

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        dp = [[0 for j in range(n+1)] for i in range(m+1)]
        for str in strs:
            cnt0 = str.count('0')
            cnt1 = str.count('1')
            for i in reversed(range(cnt0,m+1)):
                for j in reversed(range(cnt1,n+1)):
                    dp[i][j] = max(dp[i][j],dp[i-cnt0][j-cnt1]+1)
        return dp[-1][-1]
[leetcode] 2を変更518.Coin

DP配列を確立し、電流量に到達するためのいくつかの手順を保存します。、そして最後に[金額]をDPに戻ります - 私は、これはDP [i番目の量の電流量を]合算されて達成するためにどのくらいのステップjの数の合計を達することができる前にのみ参照するには量トラバーサルずつ、。

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp = [0 for i in range(amount+1)]
        dp[0] = 1
        for coin in coins:
            for i in range(coin,amount+1):
                dp[i] += dp[i-coin]
        return dp[-1]

概要

一般的なダイナミックプログラミングの問題については、以下の例は:
1。フィボナッチ数(階段を登る)
2.01ナップザック問題
3.最長共通部分列
4巡回セールスマン問題nを!

注意:
多くのルーチンタイトルに使用される動的プログラミングアルゴリズムがあり
、スクロール配列、圧縮された状態、(Lビクトリア、単調な、四角形不平等(高度なルーチン))
まず、暴力、冗長性を探して、冗長性の排除:自然

おすすめ

転載: www.cnblogs.com/hellojamest/p/11697744.html