找零钱的两种方法

news/2025/3/15 5:06:17/

有时候,去便利店买几块钱的东西,但没有零钱,只能给他们一张100的,他们可能找给我一沓10块的和几枚硬币。我不喜欢这么多的零钱,要知道,钱越零散,散失地就越快,我希望找给我的零钱张数最少。

如何找出最少数目(钱的张数)的零钱呢?这个问题看起来很简单,假设要用50、20、10、5、1(元)找出87元来,任何人都可以简单地得出:1张50、1张20、1张10、1张5和2张1元就可以满足。可以用代码表示出来:

复制代码
  
public static int[] MakeChange(int money, int[] changes) {int[] result = new int[changes.Length];for (int i = 0; i < changes.Length; i++){result[i] = money / changes[i];money = money % changes[i];if (money == 0) { break; }}return result; }
复制代码

输入money=87,changes={ 50, 20, 10, 5, 1 },则结果为:{ 1, 1, 1, 1, 2 }。这种方法的特点是:从最大额的零钱开始,逐次凑出需要的数额来,不关心总的数目是否真的是最小。这样的算法也形象地称为“贪心算法”,而找出最少数目的零钱的问题称为“最优化问题”。在某些情况下,贪心算法未必能得到最优的解,比如恰好10元和5元没有了,只剩下50、20和1元,这时要找出60元,需要1张50和10张1元,而实际上只要3张20就可以了。如果各种零钱是充足的,则可以证明贪心算法得到的解也是最优解。

零钱的集合是 { 50, 20, 10, 5, 1 },记为C。对于一个特定的数额n,它的最优解记为q。那么q中至多有2个20,因为如果有3个的话,可以用1张50和1张10来代替3张20;如果有2张20,则不能有10元的,否则可以用1张50来代替,同理最多有1张5和4张1,这样可以确定如果有2张20,则在q中小于50的零钱总数在40和49之间;同理如果只有1张20的,则最多有1张10、1张5和4张1,总数在20和39之间;如果没有20,则最多有1张10、1张5和4张1,总数在0和19之间。总之,在最优解中,小于50的零钱总数在0到49之间【结论1】。有了这个结论,下面来证明上述问题中,贪心算法得到的解也是最优解。

还是使用上面的标记:零钱集合C,数额n,最优解q,假设贪心算法得到的解是q’。用q50表示q中50元的个数,q’50表示q’中50元的个数,由于q’中包含尽可能多的50,所以q50<=q’50,由【结论1】,q50<q’50不可能成立,否则小于50的零钱总数大于49,所以一定有q50=q’50。同理也可以证明在q和q’中20、10、5、1的个数也是相同的。

如果各种零钱充足,现在是没问题了,如果某些零钱没有了,贪心算法未必能得到最优解,这时该如何求解呢?为简化问题,我们假设1元的钱总是充足的,所以解总是存在的。对于数额n,可做如下考虑:

1)如果n = 1,则用1个1元来找零,这就是最优解;

2)如果n > 1,则对于每个可能的值i,分别找出i元和n-i元来。

通过这样的递归,可以找出所有可能的解来,这样就可以找到最优解来了。不过该方法效率不高,因为存在大量冗余的计算,比如要找出60元,中间需要计算59,要计算59则一定需要计算58。。。而且这些计算要重复多次,这种情形称为“递归爆炸”。这里很像使用递归来求解Fibonacci数列一样,存在很大的效率问题。在优化Fibonacci数列的计算时,一种方法是将计算过的值存放在一个数组里,以供重复使用,这里也可以采用这样的思路。

复制代码
  
public static void MakeChangeDynamically(int money, int[] changes, int[] changesUsed, int[] lastChange) {changesUsed[0] = 0;lastChange[0] = 1;for (int dollars = 1; dollars <= money; dollars++){// 至少可以全部使用1元来找零 int minChangeCount = dollars;int newChange = 1;for (int j = 0; j < changes.Length; j++){if (changes[j] > dollars){continue; // 不能使用该数额来找零 }// 如果使用这个数额来找所需要的数目更小 if (changesUsed[dollars - changes[j]] + 1 < minChangeCount){minChangeCount = changesUsed[dollars - changes[j]] + 1;newChange = changes[j];}}changesUsed[dollars] = minChangeCount;lastChange[dollars] = newChange;} }
复制代码

这个方法属于动态规划的应用。假设现在要找的数额为67,changes = { 1, 5, 10, 20, 50 },changesUsed数组会保存从1到66之间的数值分别需要多少张零钱,在求解67的时候,会这样考虑:对于changes的每个数值,将67拆分为1+66,5+62,10+57,20+47,50+17,由于66、62、57、47、17这些值都已计算过,所以可以迅速得出对于67找零需要几张零钱;同时lastChange数组保存了从1到66之间的数值的最优解中,它们所使用的最后一张零钱是什么,这样回推过去,不但可以知道用几张零钱,还可以知道这些零钱的数额分别是什么。

虽然如此,在日常生活中找零钱的时候,两种方法都不需要,心算即可:)


http://www.ppmy.cn/news/510507.html

相关文章

零钱有限制和没有限制的找零钱问题

在参加腾讯模拟考的时候&#xff0c;其中的一道编程题是找零钱的问题&#xff0c;但是零钱的数量是一定的&#xff0c;并不是无限的。而且零钱都是2的K次幂&#xff0c;1&#xff0c;2&#xff0c;4&#xff0c;8&#xff0c;16&#xff0c;….每种零钱的数量是 2&#xff0c;给…

MySQL之误删数据如何处理

写在前面 在工作中不管是程序bug&#xff0c;运维的失误&#xff0c;等&#xff0c;都有可能导致数据误删除&#xff0c;或者是误操作&#xff0c;此时我们就必须快速的恢复数据&#xff0c;避免对正常业务造成过大的影响&#xff0c;甚至出现事故&#xff0c;本文我们按照如下…

家庭理财,轻松记账修改收支记录这样操作

我们在记账的时候难免会出现记错或者想修改的地方&#xff0c;又或者是想将之前太久没有用的记账记录删除掉&#xff0c;今天&#xff0c;小编就教大家如何修改收支记录&#xff0c;一起接着往下看吧&#xff01; 第一步&#xff0c;运行【晨曦记账本】在软件主界面中&#xff…

EXCEL如何真正彻底去掉小数点后的数字

EXCEL如何真正彻底去掉小数点后的数字 目录 EXCEL如何真正彻底去掉小数点后的数字 例如&#xff1a; &#xff08;1&#xff09;我们设置不保留小数取整&#xff0c;它的显示效果是19869&#xff0c;但实际上&#xff0c;它的值并没有变&#xff0c;还是19869.1538&#xf…

微信转账记录删除了服务器还有吗,微信转账记录能彻底删除吗?你应该知道的删除技巧是这三种!...

微信转账记录能彻底删除吗&#xff1f;有时我们需要删除微信转账记录&#xff0c;为了自己的账户安全&#xff0c;同时为了保证隐私&#xff0c;但是如何才能彻底删除转账记录呢&#xff1f;相信现在很多小伙伴肯定不知道如何彻底删除&#xff0c;不过没关系&#xff0c;小编找…

微信零钱明细删除后服务器有记录吗,微信零钱明细怎么删除记录?教你微信零钱明细记录如何删除...

微信零钱明细怎么删除记录?教你微信零钱明细记录如何删除 2017-12-21 15:32:27 来源&#xff1a;网络 扫码可以&#xff1a; 1.在手机上浏览 2.分享给微信好友或朋友圈 摘要&#xff1a; 微信零钱明细怎么删除记录?手把手教你微信零钱明细记录如何删除 大家经常会用微信支付…

无主之地2 不费子弹手枪

不费子弹手枪 BL2(hwAAAADxCACE5wRABhGL6cOIhYEQIwLG/38KGB4w/v8rxEiDgRHj) BL2(hwAAAAAwsQCE5wRABhGL6cNohYEQgwHG//8KGAkx/v8rxCiCgRHj) 转载于:https://www.cnblogs.com/nightnine/p/10499299.html

没学过编程能学python吗_无主之地2中这些拥有金钥匙的方法你知道吗?

许多朋友都喜欢玩无主之地2游戏&#xff0c;作为无主之地系列游戏的第二作&#xff0c;不仅是继承了前作精彩的玩法&#xff0c;还加入了许多新的元素&#xff0c;深受玩家们的喜爱。而在游戏里面&#xff0c;我们为了获得更多的福利和道具&#xff0c;都是需要拿到金钥匙的&am…