简单暴力到dp的优化(入门篇)
上篇,我們提到,遇到問題,首先根據(jù)定義寫出笨方法,找出依賴關系(有些題這一步就不太簡單,要自己歸納關系),然后進行優(yōu)化,下面,我們通過幾道此方面的經(jīng)典的,較為簡單的二維題目進行講解。
?
開始根據(jù)題來說明:
第一個萌新題
給定數(shù)組arr, ?arr ?中所有的值都為正數(shù)且不重復。每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張, ?再給定一個整數(shù)aim 代表要找的錢數(shù),求組成aim 的最少貨幣數(shù)。
??[舉例]
??arr=[5,2,3], ?aim=20。
??4 張5 元可以組成20 元,其他的找錢方案都要使用更多張的貨幣,返回4。
arr=[5,2,3], ?aim=0。
??不用任何貨幣就可以組成0 元,返回0。
??arr=[3,5],aim=2。
??根本無法組成2 元,錢不能找開的情況下默認返回-1。
(你要是想貪心就貪吧,反正我不貪)
定義f(a,b)代表的是a面值及之前的貨幣,組成b元的最少張數(shù)。那f(arr[-1],aim)就是我們想求的答案。現(xiàn)在分析,怎樣通過前面的狀態(tài)推出結(jié)果?對于貨幣arr[-1],我們可以一張都不用,那最少張數(shù)其實就是f(arr[-2],aim),我們也可以用一張arr[-1],最少張數(shù)就可能是f(arr[-2],aim-arr[-1])+1(用之前的錢組成aim-arr[-1]元錢,然后加一張arr[-1]),同理,我們可以用兩張arr[-1],或者更多,直到下一個就超過aim的時候。所以我們應該在所有情況中選最小的,
歸納表達式:
f(a,b)=min(f(arr[a-1],b-k*arr[a])+k),k>=0,且b-k*arr[a]>=0
當然,以前也提到過,直接遞歸我們會有大量重復計算,所以需要記下來之前的結(jié)果供我們使用。有兩個參數(shù)代表現(xiàn)在的狀態(tài),所以生成二維表。
l=[[0 for i in range(len(arr))] for i in range(aim+1)]#
我們看,f(arr[a],b)和誰有關?和上一行,也就是arr[a-1]那一行的很多左邊元素有關。
所以確定打表順序,從上到下,從左到右,打表,一個一個打,l[a][b]=min(f(a-1,b-k*arr[a])+k),依次推出l[a][b],右下角就是答案。
?
?
?
但是,還是有相當多的重復計算,我們的l[a][b-arr[a]]其實就是根據(jù)除了l[a-1][b]的左邊那些元素求的的最小值
所以l[a][b]=min(l[a][b-arr[a]]+1,l[a-1][b])。
?
其實和左邊和上邊元素相關的背包,還有一些別的題,都是如此。對于本物品,當前決策就是拿或不拿,以前的最優(yōu)情況
l[a][b-arr[a]]和l[a-1][b]已經(jīng)有了。不用管的。結(jié)合當前狀態(tài)的定義,就明白了。
?
至此,時間優(yōu)化到嚴格o(a*b),空間o(a*b),空間還能優(yōu)化到o(min(a,b)),下一個題講壓縮方法。
第二個萌新題
給一個由數(shù)字組成的矩陣,初始在左上角,要求每次只能向下或向右移動,路徑和就是經(jīng)過的數(shù)字全部加起來,求可能的最小路徑和。
1 ?3 ?5 ?9
8 ?1 ?3 ?4
5 ?0 ?6 ?1
8 ?8 ?4 ?0
路徑:1 3 1 0 6 1 0路徑和最小,返回12
生成和矩陣相同大小的二維表DP,用來記錄當前的最小路徑和
(以后也不分析暴力的時間復雜度了)
對于普遍的位置i,j,只有i-1,j和i,j-1這兩個位置可以一步走到這里,所以
DP[i,j]=min(DP[i,j-1],DP[i-1,j])+L[i,j]
壓縮:我們發(fā)現(xiàn),除了這個位置上本身的數(shù),DP[i,j]只和DP表中左邊和上邊的值有關,所以可以生成長度為矩陣較小邊長一維表,用兩層循環(huán)。注意順序,從左向右打表,只有這樣,左邊的那個元素才是被更新過的,才是本行的左邊那個元素。
最左邊的DP值是直接累加的,其他位置
For i 0 to 高度:
????For j 0 to 寬度
DP[j]=min(DP[j-1],DP[j])+L[i,j]
時間不變,空間優(yōu)化到o(較小邊長)
?
?第三道萌新題
題干和第一題一樣,請返回所有的還錢方法有多少種
經(jīng)過一二題,應該自己會做了。
(請記住:一般問方法的題目,只需把式子中max或者min改為sum即可)
n=int(input()) dp=[0]*(n+1) dp[0]=1 tmp=[1,5,10,20,50,100] for kk in tmp:for i in range(kk,n+1):dp[i]+=dp[i-kk] print(dp[n])?
總結(jié)
以上是生活随笔為你收集整理的简单暴力到dp的优化(入门篇)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode94 二叉树的中序遍历
- 下一篇: 线段树简单实现