目錄動態規劃法將待求解問題分解成若干個相互重疊的子問題,每個子問題對應決策過程的乙個階段,一般來說,子問題的重疊關係表現在對給定問題求解的遞推關係稱為動態規劃函式中,將子問題的解求解一次並填入表中,當需要再次求解此子問題時,可以通過查表獲得該子問題的解,從而避免了大量重複計算。具體的動態規劃法多種多樣,但都具有相同的填表形式。一般來說,動態規劃法的求解過程由以下三個階段組成:
劃分子問題:將原問題分解為若干個子問題,每個子問題對應乙個決策階段,並且子問題之間具有重疊關係。
確定動態規劃函式:根據子問題之間的重疊關係找到子問題滿足的遞推關係式即動態規劃函式,這是動態規劃法的關鍵。
填寫**:設計**,以自底向上的方式計算各個子問題的解並填表,實現動態規劃過程。
上述動態規劃過程可以求得問題的最優值即目標函式的極值,如果要求出具體的最優解,通常在動態規劃過程中記錄必要的資訊,再根據最優決策序列構造最優解。
在面值為 (v1,v2,…,vn) 的 n 種貨幣中, 需要支付 y 值的貨幣,應如何支付才能使貨幣支付的張數最少?例如給定 v1=1,v2=5,v3=6,v4=11,y=20,在使用 4 種貨幣換取出 20 的同時保證使用的張數最小。
要用 4 種面值的貨幣換取 20,總共有 4 種可能,分別是先換取 19 再加上一張面值 1 的貨幣、先換取 15 再加上一張面值 5 的貨幣、先換取 14 再加上一張面值 6 的貨幣、先換取 9 再加上一張面值 11 的貨幣。設 cost(n) 為換取 n 需要的最少貨幣數,則使用數學語言的描述如下:
把情況從特殊推廣到一般情況,設貨幣面值集合 v 中有表示不同面值貨幣的元素 v1,v2,v3…,vi,獲得狀態轉換方程為:
設需要換取的總金額為 y,貨幣的面值分別為 v1,v2,v3…,vi,滿足換取 y 的最少貨幣數對應到每種面值貨幣的張數為 n1,n2,n3…,ni。此時換取的總張數為 n1+n2+n3+…ni,換取的公式為:
假設從這些貨幣中拿掉一張面值為 v1 的貨幣,此時換取的總張數為 n1+n2+n3+…ni-1,換算公式為:
假設此時換取 y-v1 的總張數 n1+n2+n3+…ni-1 不是最少張數,則必然存在另一種換算方式為每種面值貨幣的張數對應 m1,m2,m3…,mi,也就是總張數為 m1+m2+m3+…mi 張。進而推出換算 y 的最少張數為 m1+m2+m3+…mi+1 張。然而已知總張數為 n1+n2+n3+…ni 為換取 y 的最少張數,不可能存在換取方式的總張數比這種方法還要少,產生了矛盾,因此找零錢問題滿足最優子結構。
想要求出總金額 y 的最少貨幣張數,就需要先算出 1—(y-1) 的最少貨幣張數,可以申請乙個一維陣列 conversion_table[y+1] 來儲存。假設我們要算金額 11 的最少貨幣張數,我們就先要算出金額 1—10 的最小貨幣張數。
根據狀態轉移方程,換取金額 11 的最少張數為:
如果想要知道具體是如何換算的,還需要乙個一維陣列 add_table[maxv] 進行輔助,該陣列用於儲存該金額相對於前驅狀態新增的貨幣面值。要獲取總金額 y 的前驅狀態新增的貨幣金額,需要訪問陣列元素 add_table[y-add_table[y]]。
總結一下解決問題的方式,可以得到演算法的偽**如下:
; //儲存貨幣不同的面值
int add_table[maxv] = {}; //新增貨幣表
int total; //總共需要的金額數
int types; //貨幣的種類數
int num; //當前所需貨幣數
int pre;
cout << "總共需要的金額:";
cin >> total;
conversion_table[0] = 0;
//初始化每種總金額的最小貨幣數
for(int i = 1; i <= total; i++)
cout << "貨幣種數:";
cin >> types;
//初始化 types 種貨幣
for(int i = 0; i < types; i++)
//計算低於總金額的每一種金額的最優換取方式
for(int i = 1; i <= total; i++)
}} }
//輸出所有金額的換取方式
演算法的時間複雜度主要由兩部分組成:第一部分是依次計算從金額 1 到 y 的各個狀態的貨幣最少張數,由兩層巢狀的迴圈組成,外層迴圈執行 n-1 次,內層迴圈分別對 i 種貨幣面值進行計算,並且在所有迴圈中,每種面值只計算一次。假定總金額數為 m,則時間效能是 o(m)。第二部分是輸出最少張數的換取方式,設換取張數為 k,其時間效能是 o(k)。綜上所述,時間複雜度為 o(m+k)。
《演算法設計與分析(第二版)》——王紅梅,胡明 編著,清華大學出版社
動態規劃5 找零錢問題
題目 有陣列penny,penny中所有的值都為正數且不重複。每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張,再給定乙個整數aim 小於等於1000 代表要找的錢數,求換錢有多少種方法。給定陣列penny及它的大小 小於等於50 同時給定乙個整數aim,請返回有多少種方法可以湊成aim。樣例...
動態規劃 找零錢問題 收藏
view plaincopy to clipboardprint?include using namespace std const int m 1000 const int n 3 int coint n int count m 1 count i 表示湊合數量為i所需最少的錢幣數量,則count...
找零錢問題 動態規劃 python
問題描述 設有n種不同面值的硬幣,各硬幣的面值存於陣列t 1 n 中。現要用這些面值的硬幣來找錢,可以實用的各種面值的硬幣個數不限。當只用硬幣面值t 1 t 2 t i 時,可找出錢數j的最少硬幣個數記為c i,j 若只用這些硬幣面值,找不出錢數j時,記c i,j 程式設計任務 設計乙個動態規劃演算...