動態規劃之硬幣兌換(Coin Change)
已知N,如果我們想要換N分,而且每種S = { S1, S2, .. , Sm} 價值的硬幣是不限數量的,那麼我們有多少種方法來兌換?硬幣的順序是無所謂的。
例如:N = 4,S = {1,2,3},,因此有四種答案: {1,1,1,1},{1,1,2},{2,2},{1,3}。所以輸出應該是4。N = 10,S = {2, 5, 3, 6},因此有四種答案: {2,2,2,2,2},{2,2,3,3},{2,2,6},{2,3,5}和{5,5}。所以輸出應該是5。
1)最優的子結構
想得到答案的總數,我們可以將所有的方法分成兩部分。
1)方案中不包含第m種硬幣(或者Sm)
2)方案中至少包含一個Sm
設count(S[], m, n) 是解決方案的總數,那麼它可以寫為count(S[], m-1, n)與count(S[], m, n-Sm)的和。
所以這個問題具有最優子結構屬性,可以通過解決子問題來解決。
2)重複的子問題
下面是硬幣兌換問題的一個簡單遞迴實現,這個實現是根據上述的遞迴結構來寫的。
#include<stdio.h>
// Returns the count of ways we can sum S[0...m-1] coins to get sum n
int count( int S[], int m, int n )
{
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
return 1;
// If n is less than 0 then no solution exists
if (n < 0)
return 0;
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
return 0;
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n ) + count( S, m, n-S[m-1] );
}
// Driver program to test above function
int main()
{
int i, j;
int arr[] = {1, 2, 3};
int m = sizeof(arr)/sizeof(arr[0]);
printf("%d ", count(arr, m, 4));
getchar();
return 0;
}
你應該能發現有的子問題已經被重複計算了,請看下面S = {1, 2, 3},n = 5的遞迴樹。
C({1}, 3)被呼叫了兩次,如果我們把樹完全畫出來,那麼我們可以看到有許多子問題都重複計算了。
C() --> count()
C({1,2,3}, 5)
/ \
/ \
C({1,2,3}, 2) C({1,2}, 5)
/ \ / \
/ \ / \
C({1,2,3}, -1) C({1,2}, 2) C({1,2}, 3) C({1}, 5)
/ \ / \ / \
/ \ / \ / \
C({1,2},0) C({1},2) C({1,2},1) C({1},3)C({1}, 4) C({}, 5)
/ \ / \ / \ / \
/ \ / \ / \ / \
. . . . . . C({1}, 3) C({}, 4)
/ \
/ \
. .
因為相同的問題又被呼叫了,這個問題有重複子問題屬性,所以這個硬幣兌換問題同時具有動態規劃問題的兩個屬性。就像其他典型的動態規劃問題一樣,可以通過自底向上地建立一個臨時陣列table[][]來避免子問題的重複計算。
動態規劃問題答案
#include<stdio.h>
int count( int S[], int m, int n )
{
int i, j, x, y;
// We need n+1 rows as the table is consturcted in bottom up manner using
// the base case 0 value case (n = 0)
int table[n+1][m];
// Fill the enteries for 0 value case (n = 0)
for (i=0; i<m; i++)
table[0][i] = 1;
// Fill rest of the table enteries in bottom up manner
for (i = 1; i < n+1; i++)
{
for (j = 0; j < m; j++)
{
// Count of solutions including S[j]
x = (i-S[j] >= 0)? table[i - S[j]][j]: 0;
// Count of solutions excluding S[j]
y = (j >= 1)? table[i][j-1]: 0;
// total count
table[i][j] = x + y;
}
}
return table[n][m-1];
}
// Driver program to test above function
int main()
{
int arr[] = {1, 2, 3};
int m = sizeof(arr)/sizeof(arr[0]);
int n = 4;
printf(" %d ", count(arr, m, n));
return 0;
}
輸出:
4
時間複雜度:O(mn)
下面是方法2的簡單版本,只需要O(n)的附件空間。
int count( int S[], int m, int n )
{
// table[i] will be storing the number of solutions for
// value i. We need n+1 rows as the table is consturcted
// in bottom up manner using the base case (n = 0)
int table[n+1];
// Initialize all table values as 0
memset(table, 0, sizeof(table));
// Base case (If given value is 0)
table[0] = 1;
// Pick all coins one by one and update the table[] values
// after the index greater than or equal to the value of the
// picked coin
for(int i=0; i<m; i++)
for(int j=S[i]; j<=n; j++)
table[j] += table[j-S[i]];
return
相關推薦
動態規劃之硬幣兌換(Coin Change)
已知N,如果我們想要換N分,而且每種S = { S1, S2, .. , Sm} 價值的硬幣是不限數量的,那麼我們有多少種方法來兌換?硬幣的順序是無所謂的。 例如:N = 4,S = {1,2,3},,因此有四種答案: {1,1,1,1},{1,1,2},
動態規劃之揹包問題(C語言)
動態規劃 動態規劃(英語:Dynamic programming,簡稱DP)是一種通過把原問題分解為相對簡單的子問題的方式求解複雜問題的方法。 動態規劃常常適用於有重疊子問題和最優子結構性質的問題 動態規劃思想大致上為:若要解一個給定問題,我們需要解其不同
經典動態規劃之放蘋果(洛谷P2386)
傳送門 題目背景 (poj1664) 題目描述 把M個同樣的蘋果放在N個同樣的盤子裡,允許有的盤子空著不放,問共有多少種不同的分發(5,1,1和1,1,5是同一種方法) 輸入輸出格式 輸入格式: 第一行是測試資料的數目t(0 <= t <= 20),以下每行均
基礎演算法——動態規劃之硬幣問題
""" 我們有面值為1元3元5元的硬幣若干枚,如何用最少的硬幣湊夠11元? 1 求問題的最優解:最小的硬幣數 2 是否有子問題:coin(n)表示的最少硬幣數是是上一次拿時候的硬幣數最少。 注意:coin(n)是n元的最小硬幣數,最後一次可拿的硬幣數為1,3
動態規劃之硬幣面值組合問題
問題描述 假設我們有8種不同面值的硬幣{1,2,5,10,20,50,100,200},用這些硬幣組合夠成一個給定的數值n。例如n=200,那麼一種可能的組合方式為 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1
動態規劃-狀壓DP(炮兵陣地)
我軍 如何 tro 輸入 可見 php 正整數 接下來 一個 P2704 炮兵陣地 題目描述司令部的將軍們打算在NM的網格地圖上部署他們的炮兵部隊。一個NM的地圖由N行M列組成,地圖的每一格可能是山地(用“H” 表示),也可能是平原(用&ldquo
hdu2069(Coin Change)
algo input mat suppose des nsis memset ace ins 題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=2069 Coin Change Time Limit: 1000/1000 MS (
動態規劃演算法學習總結(帶案例)
【動規演算法學習總結】 首先,遇到動態規劃問題要找到三個重要元素: 1.最優子結構 2.邊界 3.狀態轉移方程 【最優子結構】 通俗來說,就是具有規律性的結果的獲取方式。 如上樓梯問題
【動態規劃】機器分配 (ssl 1639)
機 器 分 配
【動態規劃】石子合併 (ssl 2863)
石 子 合 並
【動態規劃】大廳安排 (ssl 1212)
大 廳 安 排
【動態規劃】拔河比賽 (ssl 1638)
拔 河 比 賽
動態規劃——DP算法(Dynamic Programing)
最優 n) 每次 for 利用 列表 自底向上 python bubuko 一、斐波那契數列(遞歸VS動態規劃) 1、斐波那契數列——遞歸實現(python語言)——自頂向下 遞歸調用是非常耗費內存的,程序雖然簡潔可是
leetcode322 Coin Change (動態規劃之 湊錢問題 - 1)
湊錢問題可以說是一類非常經典的演算法了, 也就是給你一堆相應面額的錢幣, 要求用最少的錢幣數湊出一個數, 通常會用貪心和動態規劃兩種演算法 用貪心演算法的情況一般只有一種, 就是各個面額之間沒有公因子的情況, 比如人民幣, 就分為1, 5, 20, 50, 100; 這樣的話每次就儘量取最大的就
動態規劃入門-完全揹包(硬幣兌換問題)
C - 完全揹包在一個國家僅有1分,2分,3分硬幣,將錢N兌換成硬幣有很多種兌法。請你程式設計序計算出共有多少種兌法。Input每行只有一個正整數N,N小於32768。Output對應每個輸入,輸出兌換方法數。Sample Input2934 12553Sample Outp
動態規劃之最長遞增子序列(LIS)
lib sca while -c -o 組成 describe log ret 在一個已知的序列{ a1,a2,……am}中,取出若幹數組成新的序列{ ai1, ai2,…… aim},其中下標 i1,i2, ……im保持遞增,即新數列中的各個數之間依舊保持原
動態規劃之最長公共子序列(LCS)
int tdi -s can 數組下標 include har 遞推 最長公共子序列 在字符串S中按照其先後順序依次取出若幹個字符,並講它們排列成一個新的字符串,這個字符串就被稱為原字符串的子串 有兩個字符串S1和S2,求一個最長公共子串
動態規劃之完全揹包問題(java實現)
之前寫了01揹包問題,現在寫完全揹包問題。和01揹包不同的是,完全揹包不限定某種物品的件數,可以裝0,1,2,...,而01揹包只有裝與不裝的區別。但是思考問題的方式還是一樣的,我就其中的最大值。詳細程式碼和註釋見下面程式碼。 package backpack; /* f[i][v]:前i件物
動態規劃之0-1揹包問題(POJ3624)
有N件物品和一個容積為M的揹包。第i件物品的體積w[i],價值是d[i]。求解將哪些物品裝入揹包可使價值總和最大。每種物品只有一件,可以選擇放或者不放。(N<=3500,M<=130000)。 解題思路: 用F[i][j]表示取前i種物品,使它們總體積不超過j的最優取法取
SDNUOJ 1292 動態規劃之LDS(最長下降子序列)
Description 昨天是平安夜,聖誕老人從房頂上的煙囪裡爬到小朋友床邊把禮物送給夢鄉中的小朋友,但是今年的聖誕老人是處女座的,他有很嚴重的強迫症,他從一條街的一端開始,每次送禮物進的煙囪都不能比之前進的煙囪高,而且他還想要送出最多的禮物。 Input 輸入資料只有一行,該行包含若干個資