问题描述:小光有三种硬币,分别面值c1,c2,c3元,每种硬币的数量足够多。小光去书店买一本书耗费n元的书,如何用最少个数的硬币正好付清,不需要店主找钱,若满足,输出最少的硬币个数;若不满足,输出0。
分析:
1.假设面额为1,2,5元,n=16;则最少的个数为4,即 1+5*3=16。
2.小光付钱的过程中显然最后付的一个硬币数值有三种情况,分别是 1,2,5。假设数组 dp[i] 表示已付金额为 i 时使用的最少硬币个数,那么上述过程可以描述为:
1 | dp[16] = dp[16-1]+1 |
---|---|
2 | dp[16] = dp[16-2]+1 |
3 | dp[16] = dp[16-5]+1 |
anwer | dp[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] = max | i = 1,2,…,n |
---|
5.计算:
步骤 | 计算 |
---|---|
初始 | dp[0] = 0 |
step 1 | dp[1] |
step 2 | dp[2] |
… | … |
step n | dp[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语言时,在慕课上看到的一道有关动态规划的题,就想从动态规划开始,中午看了几道相关的题如走到右下角的不同路径数,跳台阶跳法,走到右下角路径上的数字最小,硬币个数最少。挺有趣。