有若干币值为1元、3元、5元的硬币,问得到11块钱最少需要几枚硬币。咋一看,用一眼望穿法就可知道是3枚,但我们要用程序来实现它。
就像做证明题一样,我们将这个问题细分,从最简单的得到1元需要几枚硬币开始分析,很显然coinValue(1)=1,只需要拿一个1元硬币即可。(若从0元算起,则有coinValue(0) = 0),要得到2元,我们只有1元面值的硬币可选择,我们先拿一个1元的硬币,还剩下一元面值,从coinValue(1)=1可得到coinValue(2)=coinValue(1)+1,要得到3元,有1元和3元两种面值的硬币可供选择,假设,我们先拿一枚1元的,则还剩2元,即coinvalue(3)=coinValue(2)+1,假设先拿一枚3元的硬币,则剩下0元,即coinvalue(3)=coinValue(0)+1(这里的+1,代表的是先拿的那一枚硬币),两种取法取最小值coinValue(3)=min{coinValue(2)+1,coinValue(0)+1},由此可推,coinValue(i)=coinValue(i-vj)+1,i代表要得到的钱,vj表示第j枚取走的硬币的面值。因为存在多种取法,所以要取所有方法中的最小值。可以看出,这里存在递归。
先贴段递归的代码:
C#代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BubbleSort
{
class Program
{
static void Main(string[] args)
{
int count = 0;
int re = CoinSort(11,ref count);
Console.WriteLine("得到11枚硬币至少需要:" + re+"枚硬币,执行的次数:"+count);
Console.ReadLine();
}
public static int CoinSort(int n,ref int count)
{
count++;
int min;
if (n < 3)
{
min = n;
}
else
{
min = CoinSort(n - 1, ref count) + 1;//先取一枚1元的硬币,需要的硬币数
int min3 = CoinSort(n - 3, ref count) + 1;//先取一枚3元的硬币,需要的硬币数
if (min > min3)
{
min = min3;
}
if (n >= 5)
{
int min5 = CoinSort(n - 5, ref count) + 1;
if (min > min5)
{
min = min5;
}
}
}
return min;
}
}
}
很显然,这个效率是很低的。(代码不是最优)
下面再来看看采用递推的方式
C#代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BubbleSort
{
class Program
{
static void Main(string[] args)
{
int[] coinVlue = new int[3] {1,3,5};
CoinDp(11, coinVlue);
}
public static void CoinDp(int n, int[] coinValue)
{
int count = 0;
int[] min = new int[n + 1]; //用来存储得到n块钱需要的硬币数的最小值
min[0] = 0;
for (int i = 1; i <= n; i++)
{
min[i] = int.MaxValue;//初始化数组中的每个值都是最大的整数
for (int j = 0; j < coinValue.Length; j++)
{
count++;
if (i >= coinValue[j] && min[i] > min[i - coinValue[j]] + 1)
{
min[i] = min[i - coinValue[j]] + 1;
}
}
Console.WriteLine("获取"+i+"块钱,最少需要的硬币数:"+min[i]+",执行的次数:"+count);
}
Console.ReadLine();
}
}
}
从运行结果可知,递推的方式解决硬币问题,效率更高。