動態規劃之最少硬幣湊錢問題

#include <iostream>
using namespace std;

/**
題目描述:如果我們有面值爲1元、3元和5元的硬幣若干枚,最少要用多少枚硬幣湊夠n元?
*/

/**
解題分析:
(1)要算n元要多少個湊完[f(n)],那我分類嘛:
     1.首先拿一個1元的硬幣,所以這個分類所需最少硬幣數爲f(n-1) + 1
	 2.首先拿一個3元的硬幣,這個分類所需最少硬幣數爲f(n-3) + 1
	 3.首先拿一個5元的硬幣,這個分類所需最少硬幣數爲f(n-5) + 1
	 對着三種分類分別求其湊的硬幣數,然後取最小值就是最終結果:
	 result = min{ f(n-1)+1, f(n-3)+1, f(n-5)+1 };
(2)然後就很容易了,就是遞歸,只要設置好遞歸結束的邊界條件即可
*/

int min(int a, int b, int c)
{
	int min = a;
	if(b < min)
		min = b;
	if(c < min)
		min = c;
	return min;
}

//普通遞歸法/分治法
int recursion(int n)
{
	if(n == 5 || n == 3 || n == 1)
		return 1;
	if(n == 4 || n == 2)
		return 2;
	if(n <= 0)
		return 0;

	//最優子結構
	return min(recursion(n - 1) + 1,
		       recursion(n - 3) + 1,
			   recursion(n - 5) + 1);
}

/**
事實上在《算法導論》一書中強調動態規劃算法核心點在於兩個:
(1)最優子結構
(2)重疊子問題
上面只是滿足了最優子結構,但是重疊的子問題重複計算了;
把這部分效率也進行優化才能算是真正的動態規劃算法,
否則只是普通遞歸/分治法。
*/

//避免重複計算子問題->將子問題的解保存下來
int F[20]={0};
int Dynamic_Programming(int n)
{
	if(n == 5 || n == 3 || n == 1)
		return 1;
	if(n == 4 || n == 2)
		return 2;
	if(n <= 0)
		return 0;

	if(F[n] != 0)//表明已經計算過該子問題
		return F[n];
	//記錄子問題的解,然後才return
	F[n] = min(Dynamic_Programming(n - 1) + 1,
		       Dynamic_Programming(n - 3) + 1,
			   Dynamic_Programming(n - 5) + 1);
	return F[n];
}

int main()
{
	int n;
	while(cin>>n)
		cout<<"When n = "<<n<<" ,result is: "<<Dynamic_Programming(n)<<endl;

	return 0;
}
/**
示例輸入輸出:
-1
When n = -1 ,result is: 0
0
When n = 0 ,result is: 0
1
When n = 1 ,result is: 1
2
When n = 2 ,result is: 2
3
When n = 3 ,result is: 1
4
When n = 4 ,result is: 2
5
When n = 5 ,result is: 1
6
When n = 6 ,result is: 2
7
When n = 7 ,result is: 3
8
When n = 8 ,result is: 2
9
When n = 9 ,result is: 3
10
When n = 10 ,result is: 2
11
When n = 11 ,result is: 3
*/


關於記錄子問題的解從而避免重複計算,還可看動態規劃的另一個簡單例子:

更高效的Fibonacci求解

發佈了170 篇原創文章 · 獲贊 64 · 訪問量 52萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章