动态规划算法--硬币选择问题

目录

引入

递归代码实现

非递归代码实现


引入

问题描述:有1,3,5分面额的硬币,给定一个面值11,问组成给定面值所需最少硬币的数量是多少?

方法一:采用递归解此问题

如上图,我们看到可以将面值11分成很多更小的面值来进行解决,在划分过程中我们可以看到有很多同样的子问题出现,例如第2行的子问题[6]在第4行就出现了2次,如果我们在实现过程中忽略此重复情况将会大大降低实现的效率。

递归代码实现

#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int n = 11;
int dp[n + 1] = { 0 };//组成价值n需要的最少硬币数量
int cnt = 0; //代码测试,测试一共有多少个子问题会被重复求解

int func(int n)
{
	
	if (dp[n] > 0)//此问题最优解已经被求解过了
	{
		cnt++;
		return dp[n];
	}
	if (n == 1 || n == 3 || n == 5)
	{
		dp[n] = 1;
		return 1;
	}
	else if (n == 2 || n == 4)
	{
		dp[n] = 2;
		return 2;
	}
	else
	{
		int n1 = func(n - 1) + 1;//选择了一分的硬币 后边+1表示选择了一个硬币 返回的是硬币的数目
		int n2 = func(n - 3) + 1;//选择了三分的硬币
		int n3 = func(n - 5) + 1;//选择了五分的硬币
		dp[n] = min({ n1, n2, n3 });
		return dp[n];
	}
}
int main()
{

	int num = func(n);

	cout << "num = " << num <<endl;
	cout << "cnt = " << cnt << endl;
	system("pause");
	return 0;
}

结果如下:

cnt用于测试代码,测试一共有多少个子问题会被重复求解,由于我们测试的代码是11,所以重复求解的子问题并不多,但如果是一个大数,那么重复求解的子问题会非常的多,代码效率也会很低。

方法二:采用非递归解此问题(也是动态规划常规思想)

首先设置一个数组dp[n+1],用于记录组成面值i,需要的硬币数量。

 如上图,可以推导出状态转移方程为dp[i] = min(1+dp[i-vj]) (vj代表的是硬币的面额  必须i>=vj)

非递归代码实现

int main()
{
	int v[] = { 1, 3, 5 };
	int c = 11;
	int length = sizeof(v) / sizeof(v[0]);
	int *dp = new int [c + 1]();
	for (int i = 1; i <= c; ++i)
	{
		dp[i] = i;
		for (int j = 0; j < length; ++j)
		{
			if (i >= v[j] && dp[i] > (1 + dp[i - v[j]]))
			{
				dp[i] = 1 + dp[i - v[j]];
			}
		}
	}

	cout << "num : " << dp[c] << endl;
	delete[]dp;

	system("pause");
	return 0;
}

结果如图:

 我们可以看到,两次的结果都是3。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值