动态规划-硬币个数最少

问题描述:小光有三种硬币,分别面值c1,c2,c3元,每种硬币的数量足够多。小光去书店买一本书耗费n元的书,如何用最少个数的硬币正好付清,不需要店主找钱,若满足,输出最少的硬币个数;若不满足,输出0。

分析:
1.假设面额为1,2,5元,n=16;则最少的个数为4,即 1+5*3=16。
2.小光付钱的过程中显然最后付的一个硬币数值有三种情况,分别是 1,2,5。假设数组 dp[i] 表示已付金额为 i 时使用的最少硬币个数,那么上述过程可以描述为:

1dp[16] = dp[16-1]+1
2dp[16] = dp[16-2]+1
3dp[16] = dp[16-5]+1
anwerdp[16] = 1 + min { dp[15] , dp[14] , dp[11] }

3.当硬币面额为c1,c2,c3 ,耗费n元时 :

dp[n]=1+min { dp[n-c1] , dp[n-c2] , dp[n-c3] }

显然有:

dp[0]=0

4.注意可能存在某种情况 dp[i]求不出数值,如 面额:2 5 7 ,n=10, dp[6]就求不出来。这里的处理方法就是如果dp[i]求不出来,令dp[i]=max。初始时令 :

dp[0] = 0 , dp[i] = maxi = 1,2,…,n

5.计算:

步骤计算
初始dp[0] = 0
step 1dp[1]
step 2dp[2]
step ndp[n]

6.输出:

dp[n] != max输出 dp[n]
dp[n] == max输出 0

完整代码展示:

#include <iostream>
using namespace std;
#define max 100000

//存储三种硬币的面额
int coin[3];
//dp[i]的意义为付i元时所需的最少硬币个数
int dp[1000];

 //求a,b中的最小值
int min(int a,int b)
{
    return a>b?b:a;
}
int dpf(int n)
{
    int i,j;
    int c1,c2,c3;
    cin>>c1>>c2>>c3;
    coin[0]=c1;
    coin[1]=c2;
    coin[2]=c3;
    //初始化
    dp[0]=0;
    for(i=1;i<=n;i++)
       dp[i]=max;
    for(i=1;i<=n;i++)
    {
        //三种金额的硬币都试,取个数最小
        for(j=0;j<=2;j++)
        {
            //只有金额i>= 硬币值 且 dp[i-硬币值]可求时才有可求dp[i]
            if(i>=coin[j]&&dp[i-coin[j]]!=max)
            {
                dp[i]=min(dp[i],1+dp[i-coin[j]]);
            }
        }
    }
    if(dp[n]!=max)
      return dp[n];
    return 0;
}
int main()
{
    int n;
    cin>>n;
    int num;
    num=dpf(n);
    cout<<num;
}

总结:
1.确定数组的含义。
2.找出数组元素的关系,确定递归关系式。
3.确定初始值。
4.写代码。

下了一周多的雨,终于停了,心情很舒坦,想学习一些基本的算法,想到之前学习C语言时,在慕课上看到的一道有关动态规划的题,就想从动态规划开始,中午看了几道相关的题如走到右下角的不同路径数,跳台阶跳法,走到右下角路径上的数字最小,硬币个数最少。挺有趣。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

T1009∞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值