2020天梯賽補題
阿新 • • 發佈:2020-12-05
已知問題規模為n的前提A,求解一個未知解B。
- 對於Ai+1,只需要Ai即可求解,不需更前序的狀態。這一模型稱為馬爾科夫模型。對應的推理過程稱為“貪心法”;
- 對於Ai+1,需要所有前序狀態才能完成求解。這一模型稱為高階馬爾科夫模型。對應的推理過程稱為“動態規劃法”。
能用動態規劃解決的問題的特點
- 最優化原理:若問題所包含的子問題的解也是最優的,則稱該問題具有最優子結構;
- 無後效性:某階段一旦確定,不受該決策後續決策影響,即某狀態只與當前狀態有光;
- 有重疊子問題:一個子問題在下一階段的決策中可能被多次用到(非必要但是是動態規劃演算法相對於其餘演算法的優勢)。
動態規劃演算法一般思路
- 劃分階段
- 確定狀態和狀態變數:狀態的選擇要滿足無後效性;
- 寫出狀態轉移方程:根據相鄰兩狀態之間的關係確定狀態轉移方程;
- 尋找邊界條件:狀態轉移方程的終止條件或邊界條件。
- 動態規劃的三要素為———問題階段;每個階段的狀態;狀態轉移方程;
- 確定了三要素之後,求解過程可用一個最優決策表描述。最優決策表的“行”表示決策階段;“列”表示問題狀態;表格內容為該問題對應在該階段該裝填下的最優值。
典型示例
- 斐波那契數列求解:
def fib(n): ans=[0,1] for i in range(2,n+1): ans.append(ans[-1]+ans[-2]) return ans[-1] if __name__=='__main__': n=int(input("請輸入所求解的資料位置:")) print(fib(n))
2.陣列最大不連續遞增子序列
def MaxChildArrayOrder(nums): n=len(nums) # ans[i]表示[1,i]序列中包含i在內的最大非連續遞增子序列長度 ans=[1]*(n) # 儲存結果 maxNums=1 # 若nums[i]>nums[j],ans[i]=max(ans[i],ans[j]+1) for i in range(2,n): for j in range(1,i): if nums[j]<nums[i] and ans[i]<ans[j]+1: ans[i]=ans[j]+1 maxNums= max(ans[i],maxNums) return maxNums if __name__=='__main__': nums=[3,1,4,1,5,9,2,6,5] aNums=[-1]+nums print(MaxChildArrayOrder(aNums)) >>>4
- 陣列最大連續子序列和
def MaxChildArraySum(nums):
n=len(nums)
# 最大子序列和
maxSum=nums[0]
# sums[i]表示包含nums[i]在內的最大子序列和
sums=[nums[0]]
for i in range(1,n):
sums.append(max(nums[i],nums[i]+sums[i-1]))
maxSum=max(maxSum,sums[i])
return maxSum
if __name__=='__main__':
nums=[6,-1,3,-4,-6,9,2,-2,5]
print(MaxChildArraySum(nums))
>>>14
- 三角形最小路徑和
三角形最小路徑和
def minimumTotal(triangle):
# 二維陣列dp[i][j]表示從頂點到triangle[i][j]的最小路徑
dp=[[triangle[0][0]]]
n=len(triangle)
for i in range(1,n):
temp=[]
for j in range(i+1):
# 第1列的狀態值只由上一行第一列決定
if j==0:
temp.append(dp[i-1][0]+triangle[i][0])
# 最後一列的狀態值只由左上角數的狀態值決定
elif j==i:
temp.append(dp[i-1][j-1]+triangle[i][j])
# 其餘由上方或左上角資料狀態值決定
else:
temp.append(max(dp[i-1][j-1],dp[i-1][j])+triangle[i][j])
dp.append(temp)
return max(dp[n-1])
由於二維陣列中存放的是所有的結果,但我們只需要最後一行的值,故可以考慮用一維陣列代替二維陣列,每個階段,用當前陣列(上一階段的結果)更新求得此階段的結果。
滾動陣列求解:
def minimumTotal(triangle):
n=len(triangle)
dp=triangle[[0][0]]+[0]*(n-1)
for i in range(1,n):
dp[i]=dp[i-1]+triangle[i][i]
for j in range(i-1,0,-1):
dp[j]=min(dp[j-1],dp[j])+triangle[i][j]
dp[0]=dp[0]+triangle[i][0]
return min(dp)
- 揹包問題
在N件物品取出若干件放在容量為W的揹包裡,每件物品的體積為W1,W2……Wn(Wi為整數),與之相對應的價值為P1,P2……Pn(Pi為整數),求揹包能夠容納的最大價值。
# 物體的重量列表為weight(0~n+1),價值列表為property(0~n+1)[注意weight和property需要提前擴充處理],揹包空間為room
def PackageMaxValue(weight,property,room):
length=len(weight)
# dp[i][j]表示w1-wi物品填充空間為j的揹包時,最大的價值
# dp的第0行和第0列為0
dp=[[0]*(room+1)]*(length)
for i in range(1,length):
for j in range(1,room+1):
# 若當前空間可以容納wi,考慮放進wi或者不放wi
if j>weight[i]:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+property[i])
# 若當前空間不可容納wi,不放
else:
dp[i][j]=dp[i-1][j]
return dp[-1][-1]
滾動陣列空間優化:
# 引數同上
def PackageMaxValue(w,p,v):
length=len(w)
# dp[j]表示當前階段(即物品有w1~wi)填充空間為j的揹包時最大價值
dp=[0]*v
for i in range(1,len(w)):
for j in range(v,w[i]-1,-1):
dp[j]=max(dp[j],dp[j-w[i]]+p[i])
return dp[-1]
5+. 一和零
一和零
與揹包問題的區別在於,這有兩個容量限制條件——0和1的個數,故相對傳統揹包問題,需要將一維的容量狀態變為二維
滾動陣列:
import numpy as np
def findMaxForm(strs, m, n):
dp=np.zeros((m+1,n+1),dtype=int)
for i in range(len(strs)):
zeros,ones=countZerosAndOnes(strs[i])
for j in range(m,zeros-1,-1):
for k in range(n,ones-1,-1):
dp[j][k]=max(dp[j][k],dp[j-zeros][k-ones]+1)
return dp[m][n]