动态规划(DP)---- 硬币问题(1)

最少硬币组合问题:

先看一道例题,有5种硬币类型(1分,5分,10分,25分,50分),数量无限多,输出最少的硬币组合情况

对于这道题来分析,我们可以跟完全背包问题取得些许的联系,五种硬币对应的是五个物品,而五个硬币所占金额对应的是物品的重量。那么我们通过我的上一篇文章学习的滚动数组的思想,我们可以得到状态方程为:

dp[j] = min(dp[j],dp[j - w[i]] + 1)

其中的dp数组代表的含义为最少的硬币组合情况

那么如何初始化呢?我们不难发现dp[0]的含义是选第0个硬币所对应的最少组合,那么也就是说明其组合情况肯定为0,所以dp[0] = 0。

之后我们考虑遍历顺序问题,在01背包中每件物品的选择最多选择一个,也就是说如果我们要最多选择一件物品的话,那我们应该从大到小遍历,那么细心的网友一定会发现我们从小到大遍历的情况在我上篇文章讲到,会有重复选择的情况,且要是满足最大的情况也就是不限定选择数量,那么这里我们在分析这道题的时候就应该从小到大进行遍历,才能保证可以重复使用(这个也就是后期会讲到的完全背包问题),那么我们先来看一下最小组合数的输出代码吧......

#include <stdio.h>
const int N = 1000;
int w[N];
int dp[N];//最小组合数
int money;
int min(int a ,int b)
{
	int res = a < b ? a : b;
	return res;
}
int main()
{
	scanf("%d",&money);
	for(int i = 1;i <= 5;i++)
	{
		scanf("%d",&w[i]);
	}
	for(int i = 0;i <= money;i++)
	{
		dp[i] = 1e9;//因为要最小值,所以这里的dp数组要全部初始化为较为大的数
	}
	dp[0] = 0;
	for(int i = 1;i <= 5;i++)
	{
		for(int j = w[i];j <= money;j++)
		{
			dp[j] = min(dp[j],dp[j - w[i]] + 1);
		}
	}
	printf("%d",dp[money]);
	return 0;
}

下面我们再来解决一下如何打印其最小组合吧(思考)。

在思考如何打印最小组合,我们先建立一个record数组存最小组合方案,我们想要打印就要先研究如何存入数进去,我们分析这道题的递推过程是以状态转移方程进行递推的过程,那么我们可以以状态转移方程为外围框架,内部框架就是向record数组存数,下面看一下代码思考一下吧....

if(dp[j] > (dp[j - w[i]] + 1))//如果出现需要利用当前枚举的硬币的
{
	record[j] = w[i];//存入所组合进去的硬币价值
    dp[j] = dp[j - w[i]] + 1;
}//由状态方程演变出来的
else    dp[j] = dp[j];

如果我们的dp[j] > dp[j - w[i]] + 1的话是不是代表选取了当前枚举的硬币放入到方案内,要是小于是不是保持原方案,所以要加入一个判断的过程来进行存数....

那么见完整代码来分析思考一下吧......

#include <stdio.h>
const int N = 1000;
int w[N];
int dp[N];//最小组合数
int money;
int record[N];//记录最小组合的方案
void conbition()
{
	for(int i = 0;i <= money;i++)
	{
		dp[i] = 1e9;//因为要最小值,所以这里的dp数组要全部初始化为较为大的数
	}
	dp[0] = 0;	
	for(int i = 1;i <= 5;i++)
	{
		for(int j = w[i];j <= money;j++)
	    {
		    if(dp[j] > (dp[j - w[i]] + 1))//如果出现需要利用当前枚举的硬币的情况
		    {
				record[j] = w[i];//存入所组合进去的硬币价值
			    dp[j] = dp[j - w[i]] + 1;
		    }//由状态方程演变出来的
		    else    dp[j] = dp[j];
     	}
	}
}
void print()
{
	while(money)
	{
		printf("%d ",record[money]);
		money -= record[money];//减去record[money]是为了调到存数的位置
	}
}
int main()
{
	scanf("%d",&money);
	for(int i = 1;i <= 5;i++)
	{
		scanf("%d",&w[i]);
	}
	conbition();//递推组合方案
	printf("%d\n",dp[money]);
	print();//打印组合方案
	return 0;
}

对于完全背包的一些理论基础不明白的小伙伴可以暂时当做模板,但是要是想要理解的话就把整个dp数组进行打印,加以分析,后期会讲解完全背包问题,这里先不过多解释......

好了,今天的分享到此结束啦,下期我会分享硬币问题(2),希望看到这里给个关注吧,博主会持续更新动态规划算法的相关知识,感谢观看。

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记得开心一点嘛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值