动态规划之最少硬币凑钱问题

#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求解

posted @ 2016-09-30 23:13  Victor_Lv  阅读(711)  评论(0编辑  收藏  举报