题目描述
总共有三种硬币2元,5元,7元,要求用最少的硬币数凑出27元。
1.思路
采用了动态规划的思路
-
确定状态
最重要的两点是最后一步和化为子问题;
最后一步:我们虽然不知道最优策略是什么,但是我们可以确定的是最优策略肯定是k枚硬币,a1,a2,a3…ak加起来为27.
所以一定有最后一枚硬币ak,除去这枚硬币,前边的硬币是27-ak;因为是最佳策略,所以27-ak所需的硬币数也是最少的。
化为子问题:可以通过最后一步的分析,将问题转化成求27-ak所需要的最少硬币数;
设f(27)表示所需的最少硬币数,又因为ak只有三种取值2,5,7
故可以得出关系是f(27)= min( f(27-2)+1 , f(27-5)+1 , f(27-7)+1 ); -
转移公式
由第一步可以得出数值X对应的硬币数的方程是
f(X)= min( f(X-2)+1 , f(X-5)+1 , f(X-7)+1 ); -
初始条件和边界情况
如果凑不出来就令f[i]=最大值;令f[0]=0。
ps: 如何选择初始条件–用转移公式算不出来,需要手动定义的 -
计算顺序
应从小到大进行计算,因为当计算f[x]时,f[x-2],f[x-5],f[x-7]都已经有了结果,可以直接使用。
2.代码实现(C sharp)
代码如下:
/// <summary>
/// dp求解最少硬币
/// </summary>
/// <param name="a">硬币面额数组</param>
/// <param name="m">要拼成的硬币值</param>
/// <returns></returns>
public static int CoinChange(int[] a,int m)
{
// f数组用于存放0-27所需要的最少硬币数
int[] f = new int[m + 1];
// 设置初始条件
f[0] = 0;
// 外层循环控制步数,一共27步,i代表的是要拼成的具体数值
for (int i = 1; i <=m; i++)
{
// 进行每步之前先将初始值设为最大,因为求的是最小值
f[i] = int.MaxValue;
// 内层循环控制每步中的比较次数
for (int j = 0; j < a.Length; j++)
{
// 如果要拼成的数小于硬币的面额,则跳过;如果拼不成某个数,则跳过。
if (i >= a[j] && f[i - a[j]] != int.MaxValue)
{
f[i] = Math.Min(f[i],f[i - a[j]] + 1);
}
}
}
if (f[m] == int.MaxValue)
{
f[m] = -1;
}
return f[m];
}
输出结果:5