[LeetCode] Das Prinzip und die Programmierpraxis der dynamischen Programmierung

(Datawhale hat sich zusammengetan, um im August zu studieren.) Unter den vielen Erfahrungen, die ich gesehen habe, ist die Häufigkeit der dynamischen Programmierung sehr hoch. Das Beherrschen der Idee der dynamischen Programmierung kann in der Tat viele Probleme lösen. Dieser Artikel führt hauptsächlich in das Prinzip der dynamischen Programmierung ein und löst viele Probleme in LeetCode, die durch dynamische Programmierung gelöst werden können.

【LeetCode】 Eine Artikelserie

Das Prinzip und die Programmierpraxis des Divisions- und Eroberungsalgorithmus von LeetCode Veröffentlicht am 20200819 Das Prinzip und die Programmierpraxis
der dynamischen Programmiermethode von LeetCode Veröffentlicht am 20200822

1. Das Prinzip der dynamischen Programmierung

  • Der Grundgedanke

Um ein gegebenes Problem zu lösen, müssen wir seine verschiedenen Teile (nämlich Unterprobleme) lösen und dann die Lösung des ursprünglichen Problems basierend auf den Lösungen der Unterprobleme erhalten. Dynamische Programmierung wird häufig verwendet, um rekursive Probleme zu optimieren. Wenn eine rekursive Methode verwendet wird, um viele der gleichen Unterprobleme zu lösen, kann die Idee der dynamischen Programmierung den Rechenaufwand verringern. Das dynamische Programmierverfahren löst jedes Teilproblem nur einmal und hat die Funktion des natürlichen Beschneidens, wodurch der Rechenaufwand verringert wird. Sobald die Lösung eines bestimmten Teilproblems berechnet wurde, wird es gespeichert und gespeichert, so dass beim nächsten Mal dieselbe Teilproblemlösung benötigt wird Überprüfen Sie das Messgerät direkt.

  • Schritt
  1. Bestimmen Sie den dynamischen Programmierstatus

  2. Schreiben Sie die Zustandsübergangsgleichung (zeichnen Sie die Zustandsübergangstabelle)

  3. Berücksichtigen Sie die Initialisierungsbedingungen

  4. Betrachten Sie den Ausgabestatus

  5. Berücksichtigen Sie die Optimierung der zeitlichen und räumlichen Komplexität (Bonus)


Zweitens die eigentliche Programmierung

2.1 Beispiel: Die längste aufsteigende Teilfolge von Frage 300
  • Titel Beschreibung

Bestimmen Sie bei einem ungeordneten ganzzahligen Array die Länge der längsten aufsteigenden Teilsequenz.

  • Ideen zur Problemlösung

Schritt 1: Bestimmen Sie den dynamischen Programmierstatus

Dieses Thema kann direkt ein eindimensionales Array dp verwenden, um den Übergangszustand zu speichern. Dp [i] kann als die Länge der am längsten zunehmenden Teilsequenz definiert werden, die mit nums [i] endet.


Schritt 2: Schreiben Sie eine gute Zustandsübergangsgleichung

Schreiben Sie unter Verwendung des mathematischen Induktionsdenkens eine genaue Zustandsgleichung: Vergleichen Sie die aktuelle Länge von dp [i] mit dp [i], um eine neue Teilsequenzlänge zu erzeugen. Wir verwenden j, um den Index aller Gruppen darzustellen, die kleiner als i sind. Es kann durch die folgende Codeformel ausgedrückt werden

for i in range(len(nums)):
    for j in range(i):
    	if nums[i]>nums[j]:
    		dp[i]=max(dp[i],dp[j]+1)

Schritt 3: Berücksichtigen Sie die Anfangsbedingungen

Die Grenzwertbetrachtung ist hauptsächlich in drei Stellen unterteilt:

(1) Der Anfangswert des gesamten dp-Arrays; (2) Die Positionen des zweidimensionalen dp-Arrays i = 0 und j = 0

(3) Die Länge des dp-Speicherzustands ist die Länge des gesamten Arrays oder die Länge des Arrays + 1, was besondere Aufmerksamkeit erfordert

Ergänzung: Mehrere häufig verwendete Python-Initialisierungsmethoden

# 产生全为1且长度为n的数组
dp=[1 for _ in range(n)]
dp=[1]*n

# 产生全为0,长度为m,宽度为n的二维矩阵
dp=[[0 for _ in range(n)] for _ in range(m)]
dp=[[0]*n for _ in range(m)]

Schritt 4: Betrachten Sie den Ausgangszustand

Welche Werte des Arrays werden benötigt:

(1) Geben Sie den letzten Wert im dp-Array als Ausgabe zurück, der im Allgemeinen einem zweidimensionalen dp-Problem entspricht.

(2) Geben Sie die größte Zahl im dp-Array zurück, die im Allgemeinen dem Problem des maximalen Datensatzwerts entspricht.

(3) Geben Sie den gespeicherten Maximalwert zurück, im Allgemeinen in Form von Maxval = max (Maxval, dp [i]).

Standardantwort der dynamischen Programmiermethode auf diese Frage

class Solution(object):
	def lengthOfLIS(self, nums: List[int]) -> int:
    	    if not nums:return 0  # 判断边界条件
        	dp=[1]*len(nums)      # 初始化dp数组状态
        	for i in range(len(nums)):
            	for j in range(i):
                	if nums[i]>nums[j]:   # 根据题目所求得到状态转移方程
                    	dp[i]=max(dp[i],dp[j]+1)
        	return max(dp)  # 确定输出状态

Schritt 5: Betrachten Sie die Optimierung der zeitlichen und räumlichen Komplexität (Bonus)

Die vorherige Methode zum Durchlaufen der dp-Liste erfordert O (N) O (N)O ( N ) , die Berechnung jedes dp [i] erfordertO (N) O (N)O ( N ) Zeit, also ist die GesamtkomplexitätO (N 2) O (N ^ 2)O ( N.2 ). Die zeitliche Komplexität des Durchlaufens der dp-Liste kann nicht verringert werden, aber die zeitliche Komplexität des Durchlaufens der dp [i] -Elemente von [0, i] in jeder Runde kann durch die Entwurfszustandsdefinition berücksichtigt werden, so dass das gesamte dp eine sortierte Liste ist, die die Dichotomie verwenden kann Reduzierung der Zeitkomplexität aufO (N log N) O (NlogN)O ( N l o g N )


Vorlagenzusammenfassung:

Nach der oben beschriebenen Methode können wir sie als Vorlage zusammenfassen und einen tatsächlichen Kampf durchführen:

for i in range(len(nums)):
	for j in range(i):
		dp[i]=最值(dp[i], dp[j], ...)
2.2 Aufgabe 674: Längste kontinuierlich ansteigende Sequenz
  • Titel Beschreibung

Finden Sie bei einem unsortierten Array von Ganzzahlen die längste und kontinuierlich ansteigende Sequenz.

  • Standardantwort
def findLengthOfLCIS(self, nums: List[int]) -> int:
        if not nums:return 0  # 判断边界条件
        dp=[1]*len(nums)      # 初始化dp数组状态
        # 注意需要得到前一个数,所以从1开始遍历,否则会超出范围
        for i in range(1,len(nums)): 
        	if nums[i]>nums[i-1]: # 根据题目所求得到状态转移方程
                    dp[i]=dp[i-1]+1
                else:
                    dp[i]=1
        return max(dp)  # 确定输出状态
2.3 Der längste Palindrom-Teilstring von Frage 5
  • Titel Beschreibung

Suchen Sie bei gegebener Zeichenfolge s den längsten Palindrom-Teilstring in s. Sie können davon ausgehen, dass die maximale Länge von s 1000 beträgt.

  • Standardantwort
def longestPalindrome(self, s: str) -> str:
	length=len(s)
	if length<2:  # 判断边界条件
		return s
	dp=[[False for _ in range(length)]for _ in range(length)] # 定义dp状态矩阵
	# 定义初试状态,这步其实可以省略
	# for i in range(length):
	# dp[i][i]=True
	max_len=1
	start=0 # 后续记录回文串初试位置
	for j in range(1,length):
		for i in range(j):
		# 矩阵中逐个遍历
			if s[i]==s[j]:
				if j-i<3:
					dp[i][j]=True
				else:
					dp[i][j]=dp[i+1][j-1]
			if dp[i][j]: # 记录位置,返回有效答案
				cur_len=j-i+1
				if cur_len>max_len:
					max_len=cur_len
					start=i
	return s[start:start+max_len]
2.4 Die längste Palindrom-Folge von Frage 516
  • Titel Beschreibung

Suchen Sie bei gegebener Zeichenfolge s den längsten Palindrom-Teilstring in s. Sie können davon ausgehen, dass die maximale Länge von s 1000 beträgt.

  • Standardantwort
def longestPalindromeSubseq(self, s: str) -> int:
        n=len(s)
        dp=[[0]*n for _ in range(n)]  # 定义动态规划状态转移矩阵
        for i in range(n):  # 初始化对角线,单个字符子序列就是1
            dp[i][i]=1
        for i in range(n,-1,-1):  # 从右下角开始往上遍历
            for j in range(i+1,n):
                if s[i]==s[j]:   # 当两个字符相等时,直接子字符串加2
                    dp[i][j]= dp[i+1][j-1]+2  
                else:           # 不相等时,取某边最长的字符
                    dp[i][j]=max(dp[i][j-1],dp[i+1][j])
        return dp[0][-1]   # 返回右上角位置的状态就是最长
2.5 Problem 72 Abstand bearbeiten
  • Titel Beschreibung

Berechnen Sie bei zwei Wörtern, Wort1 und Wort2, die Mindestanzahl von Operanden, die zum Konvertieren von Wort1 in Wort2 verwendet werden.

  • Standardantwort
def minDistance(self, word1, word2):
# m,n 表示两个字符串的长度
	m=len(word1) 
	n=len(word2)
	# 构建二维数组来存储子问题
	dp=[[0 for _ in range(n+1)] for _ in range(m+1)]
	# 考虑边界条件,第一行和第一列的条件
	for i in range(n+1):
		dp[0][i]=i  # 对于第一行,每次操作都是前一次操作基础上增加一个单位的操作
	for j in range(m+1):
		dp[j][0]=j # 对于第一列也一样,所以应该是1,2,3,4,5...
	for i in range(1,m+1):  # 对其他情况进行填充
		for j in range(1,n+1):
			if word1[i-1]==word2[j-1]: # 当最后一个字符相等的时候,就不会产生任何操作代价,所以与dp[i-1][j-1]一样
				dp[i][j]=dp[i-1][j-1]
			else:
				dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1 # 分别对应删除,添加和替换操作
	return dp[-1][-1] # 返回最终状态就是所求最小的编辑距离
2.6 Problem 198
  • Titel Beschreibung

Sie sind ein professioneller Dieb und planen, Häuser entlang der Straße zu stehlen. In jedem Zimmer ist eine bestimmte Menge Bargeld versteckt. Die einzige Einschränkung, die sich auf Ihren Diebstahl auswirkt, besteht darin, dass benachbarte Häuser mit miteinander verbundenen Diebstahlsicherungssystemen ausgestattet sind. Wenn zwei benachbarte Häuser gleichzeitig von einem Dieb aufgebrochen werden, alarmiert das System automatisch .

  • Standardantwort
def rob(self, nums):  
	if(not nums):   # 特殊情况处理
		return 0
	if len(nums)==1:
		return nums[0]
	n=len(nums)
	dp=[0]*n    # 初始化状态转移数组
	dp[0]=nums[0]  # 第一个边界值处理
	dp[1]=max(nums[0],nums[1]) # 第二个边界值处理
	for i in range(2,n):
		dp[i]=max(dp[i-2]+nums[i],dp[i-1]) # 状态转移方程
	return dp[-1]
2.7 Problem 213: Hausraub II
  • Titel Beschreibung

Sie sind ein professioneller Dieb und planen, Häuser entlang der Straße zu stehlen. In jedem Zimmer ist eine bestimmte Menge Bargeld versteckt. Die einzige Einschränkung, die sich auf Ihren Diebstahl auswirkt, besteht darin, dass benachbarte Häuser mit miteinander verbundenen Diebstahlsicherungssystemen ausgestattet sind. Wenn zwei benachbarte Häuser gleichzeitig von einem Dieb aufgebrochen werden, alarmiert das System automatisch .

  • Standardantwort
def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0
        elif len(nums)<=2:
            return max(nums)
        def helper(nums):
            if len(nums)<=2:
                return max(nums)
            dp=[0]*len(nums)
            dp[0]=nums[0]
            dp[1]=max(nums[0],nums[1])
            for i in range(2,len(nums)):
                dp[i]=max(dp[i-1],dp[i-2]+nums[i])
            return dp[-1]
        return max(helper(nums[1:]),helper(nums[:-1]))

Ich denke du magst

Origin blog.csdn.net/xylbill97/article/details/108169009
Empfohlen
Rangfolge