硬币组合问题之最少硬币个数

问题:如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?

此题容易让人在动态规划和贪心算法之间徘徊,毕竟对于初学者来说这两种算法要熟练掌握还是需要时间和实践的。

首先可以肯定的回答:贪心算法是行不通的。如果使用贪心算法,肯定是每次找最大的然后找次小的,如果不行再找次小的。

上述例子其实可以5元两个,1元一个共三个硬币凑够11元。但是如果输入是这种呢:5,4,1凑够8。如果用贪心则需要5,1,1,1,但实际上可以两个4元就搞定。

所以需要使用动态规划如下:

假设d(i)为凑够i元所需最少硬币数,则
d(0) = 0         理所当然


d(1) = 1         要凑够1元,需要从面值小于等于1元的硬币中选择,目前只有面值为1元的硬币
            此时d(1) = d(0) + 1


d(2) = d(2 - 1) + 1 = 2, 从面值小于等于2元的硬币中选择,符合要求的硬币面值为:1元。
            此时d(2) = d(2-1) + 1


d(3) = d(3 - 3) + 1 = 1, 从面值小于等于3元的硬币中选择,符合要求的硬币面值为:1元,3元。
            此时有有两种选择:是否选择含有面值3元的硬币
            含有3元硬币:d(3) = d(3 - 3) + 1 = 1
            不含3元硬币:d(3) = d(3 - 1) + 1 = d(2) + 1 = 3
            自然是选择二者中较小值

那这里我们加上的是哪个硬币呢。嗯,其实很简单,把每个硬币试一下就行了:
假设最后加上的是 1 元硬币,那 d(i) = d(j) + 1 = d(i - 1) + 1。
假设最后加上的是 3 元硬币,那 d(i) = d(j) + 1 = d(i - 3) + 1。
假设最后加上的是 5 元硬币,那 d(i) = d(j) + 1 = d(i - 5) + 1。
我们分别计算出 d(i - 1) + 1,d(i - 3) + 1,d(i - 5) + 1 的值,取其中的最小值,即为最优解,也就是 d(i)

static int[] coins = {1,3,5};
	public static void main(String[] args) {
		int sum = 11;//凑够11元
		int[] d = new int[sum+1];
		findmin(0,sum,d);
		for (int i = 0; i <= sum; i++) {
                   System.out.println("凑齐 " + i + " 元需要 " + d[i] + " 个硬币");
        }
		System.out.println();
		find_min(sum,d);
	}

	private static void find_min(int sum, int[] d) {
		d[0] = 0;
		for (int i = 1; i <= sum; i++) {
			int min = i;//最大个数不会超过面币价值数
			for (int coin: coins) {
				if(i>=coin && d[i-coin] + 1 < min)
				{
					min = d[i-coin]+1;
				}
			}
			d[i] = min;
		}
		for (int i = 0; i < d.length; i++) {
			System.out.print(d[i]+" ");			
		}
	}

如果要记录是哪几个硬币可以加上一个数组用来记录每次更新时候的硬币种类。如下:

private static void FindMin(int money, int[] coin) {
		int n = coin.length;
		int[] coins = new int[money+1];//存储1...money找零最少需要的硬币的个数
		int[] coinValue = new int[money+1];//最后加入的硬币,方便后面输出是哪几个硬币
		coinNum[0] = 0;
		for (int i = 1; i <=money; i++) {
			int minNum = i;//i面值钱,最少需要硬币个数
			int usedMoney = 0;//
			for (int j = 0; j < n; j++) {
				if(i>=coin[j])
				{
					System.out.print(coinValue[i-coin[j]]+" "+i+" "+coin[j]);
					if(coinNum[i-coin[j]]+1<=minNum)
					{
						minNum = coinNum[i-coin[j]]+1;//更新
						usedMoney = coin[j];
					}
					System.out.println();
				}
				
			}
			coins[i] = minNum;
			coinValue[i] = usedMoney;
		}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值