当前位置:首页 » 操作系统 » 换零钱算法

换零钱算法

发布时间: 2024-07-18 01:44:27

‘壹’ 钥冮桡细鐜嫔笀鍌呮槸鍗栭瀷镄勶纴涓鍙岄瀷杩涗环30鍏幂敥鍗20鍏冿纴椤惧㈡潵涔伴瀷缁欎简寮50锛岀帇甯埚倕娌¢浂阍憋纴浜庢槸镓鹃偦灞呮崲浜50鍏

棣栧厛浣犲亣璁句綘鍙h嬫湁100鍏冮挶锛岃繘璐х殑镞跺椤墿涓70鍏冿纸1寮20+1寮50锛夈傞【瀹㈡潵镄勬椂鍊欙纴缁欎简鐜嫔笀鍌呬竴寮50锛屾墍浠ョ幇鍦ㄦ槸锛20+50+50锛夈傜帇甯埚倕鎶婂畠鍏戞崲浜嗭纴鍙樻垚浜嗭纸20+50+10*5锛夈傚厬鎹浠ュ悗锛屾垒浜30鍏冮挶缁欓【瀹銆傜幇鍦ㄥ彉鎴愪简锛20+50+10*2锛夊彂鐜版槸锅囬挶浠ュ悗锛屽张灏戜简50锛岀幇鍦ㄥ彉鎴愪简锛20+10*2锛夈备篃灏辨槸璇村师链镄100鍏冿纴鍙樻垚浜嗙幇鍦ㄧ殑40鍏冿纴镓浠ュ叡浜忎简60鍏冦
-------鏂规硶铏界锛屼絾杩欐牱绠楃粷瀵规病阌欍
-------杩樻湁灏辨槸鐜嫔笀鍌呭师链鏄镓撶畻浜忔湰10鍏冨嚭鍞镄勶纴鐜板湪浜忔湰60鍏冿纴姣斿师璁炬兂澶氢簭链50鍏冿纴镓浠ュ傛灉鐩存帴锲炵瓟50鍏冨簲璇ヤ篃涓岖畻阌
鍏跺疄绠鍗曟潵璇村氨鏄濡傛灉闾50鏄鐪熺殑锛岄偅灏变簭10鍏冿纴鐜板湪50鏄锅囩殑锛屾墍浠ヤ簭60锛堜腑闂磋繃绋嫔畬鍏ㄤ笉鐢ㄨ冭槛锛

‘贰’ 算法:找零钱,有4种硬币1,2,5,10,将X和Y换成零钱,求所用的最少钱数 如:8,9,输出4(1,2,2,5)

这个算法相对较为简单,使用大面值硬币优先使用即可。
void getCoinList(int bigMoney)
{
int coinValues[] = {10, 5, 2, 1};
int coins[4] = {0};
int totalCoins = 0;
int surplusMoney = bigMoney;
int i = 0, j = 0;
for (i = 0; i < 4; i++)
{
coins[i] = surplusMoney / coinValues[i];
totalCoins += coins[i];
surplusMoney = bigMoney % coinValues[i];
}
printf("%d(", totalCoins);
for(i = 3; i >= 0; i--)
for(j = 0; j < coins[i]; j++)
{
if (--totalCoins > 0)
printf("%d ,", coinValues[i]);
else
printf("%d", coinValues[i]);
}
printf(")", coinValues[i]);
}

‘叁’ 100元换成10元5元1元纸币,每种至少一张,一共多少换法

81种换法
一 ,每种至少一张,实际应为至少10元、5元各一张,1元的5张。共20元,剩下的80元可任意分配。
二,80元的零钱可以如下:
8张10元。1种
7张10元,5元的2、1、0张(差额为1元,下同)。3种
6张10元,5元的4、3、2、1、0张。5种
5张10元,5元的6、5、4、3、2、1、0张。7种
......
1张10元,5元的14、13.....6、5、4、3、2、1、0张。15种
0张10元,5元的16、15、14、13.....6、5、4、3、2、1、0张。17种
三,总的换法为
1+3+5+.....+15+17=81种
看到这个题目的第一感觉就是一个三元一次方程的求解,编程的话,就是三个for循环外加个if判断,瞬间KO。对这个题目来说效率也是可以接受的。可是这根本没有体现出算法的优势。下面我们来仔细推敲下这里面隐藏的规律。 根据上图的规律,即可得到如下代码:

/**
* 1、求解特定实例:要将100元兑换为1元、5元、10元的零钱,请问有多少种兑换方法?
*
* @return
* @author chenchanghan
*/
public static int getDivideWays() {
int count = 0;
for (int i = 0, size = 100 / 10; i <= size; i++) {
// 针对10的每个场景,计算5的组合情况(即,从0个5 到 n( n=(100 - i * 10)/5
// )个5共n+1种情况
count += (100 - i * 10) / 5 + 1;
}
return count;
}

到这里,这个就算解完了,但是这里确实因为分解的元素中包含1,将问题变的简单化了,如果不是1、5、10而是随意的三个数字,改怎么解决呢?同样还是要找出规律来。

下面我们就来分析下10、5、3如何组合成100吧。

首先,0个10的情况下,5和3怎么组合成100呢?正好20*5=100,显然这是不存在10的情况下出现最多5的情况,那还有没有其他的组合情况呢?这时我们就要用到一个最小公倍数(3和5的最小公倍数是15),很显然,我们就可以将”3个5替换成5个3“了。因为最多20个5,所以我们可以继续用”3个5替换成5个3“,直到最后剩下2个5。综上0个10的情况下,5可以出现的次数分别为20、17、14、11、8、5、2,所以该场景下共有7中组合方式。

其次,1个10的情况下,5和3怎么组合成100呢?我们还是从5来出发,5*18=90,1个10的情况下,组合成100,最多可以出现18次,同理还是用”3个5替换成5个3“。最终1个10的情况下,5可以出现的次数分别为18、15、12、9、6、3、0。该场景下也有7种组合方式。

同理,依次分析下去。

根据上面的规律,得出代码如下:


/**
* 2、组合元素一般化:将total元兑换为large元、middle元、small元的零钱,请问有多少种兑换方法?
*
* @param total
* @param large
* @param middle
* @param small
* @return
* @author chenchanghan
*/
public static int getDivideWays(int total, int large, int middle, int small) {
if (total > 0 && small > 0 && middle > small && large > middle) {
int count = 0;
int LCM = getLeastCommonMutiple(middle, small);
int substituteUnit = LCM / middle;
for (int i = 0, size = total / large; i <= size; i++) {
int restTotal = total - i * large;
if (restTotal > 0) {
// actualMaxMiddleNum>=0,表示restTotal正好可以有x个middle和y个small拼凑起来(x、y是大于等于0的整数)
int actualMaxMiddleNum = getActualMaxMiddleNum(restTotal, middle, small);
if (actualMaxMiddleNum >= substituteUnit) {
// actualMaxMiddleNum >=substituteUnit,表示可以将substituteUnit个middle替换成LCM/small个small
// 可以换多少次呢?显然可以换0、1...actualMaxMiddleNum/substituteUnit,即:actualMaxMiddleNum/substituteUnit+1
count += actualMaxMiddleNum / substituteUnit + 1;
} else if (actualMaxMiddleNum >= 0) {
// 0<=actualMaxMiddleNum// 因为count++;
}
} else {
// 正好被large完美匹配了
count++;
}
}
return count;
} else {
throw new IllegalArgumentException();
}
}

/**
* 获得方程:x*middle + y*small = restTotal 中x最大的取值。
*
* @param restTotal
* @param middle
* @param small
* @return
* @author chenchanghan
*/
private static int getActualMaxMiddleNum(int restTotal, int middle, int small) {
int modMiddle = restTotal % middle;
int maxMiddleNum = restTotal / middle;
int actualMaxMiddleNum = -1;
if (modMiddle == 0 || modMiddle == small) {
actualMaxMiddleNum = maxMiddleNum;
} else {
// 无法使用最大数量(即:maxMiddleNum)的middle和small组合成restTotal,
// 则需要逐步减少middle的个数,进而增加small的个数,来尝试组合成restTotal。
int minusMiddleNum = getMinusMiddleNum(middle, small, modMiddle, maxMiddleNum);
if (minusMiddleNum > 0) {
// 表示可以形成一个拥有最大middle数的组合,即: (maxMiddleNum - minusMiddleNum)*middle + y*small = restTotal ;
actualMaxMiddleNum = maxMiddleNum - minusMiddleNum;
} else {
// middle和small无论怎么组合都无法拼凑成restTotal,即:x*middle + y*small = restTotal 的整数解不存在
actualMaxMiddleNum = -1;
}
}
return actualMaxMiddleNum;
}

/**
*
* @param middle
* @param small
* @param modMiddle
* @param maxMiddleNum
* @return
* @author chenchanghan
*/
private static int getMinusMiddleNum(int middle, int small, int modMiddle, int maxMiddleNum) {
int minusMiddleNum = -1;
for (int i = 1; i <= maxMiddleNum; i++) {
if ((middle * i + modMiddle) % small == 0) {
minusMiddleNum = i;
break;
}
}
return minusMiddleNum;
}

/**
* 求两个数的最小公倍数。
*
* @param middle
* @param small
* @return
* @author chenchanghan
*/
private static int getLeastCommonMutiple(int m, int n) {
return m * n / getGreatestDivisor(m, n);
}

/**
* 求两个数的最大公约数。
*
* @param m
* @param n
* @return
* @author chenchanghan
*/
private static int getGreatestDivisor(int m, int n) {
int tmp = 0;
if (m < n) {
tmp = m;
m = n;
n = tmp;
}
while ((tmp = m % n) != 0) {
m = n;
n = tmp;
}
return n;
}我们再来推广下,将分解的元素变成3个以上,具体见如下代码:

/**
* 3、元素个数一般化:将total元兑换为a元、b元、c元、....的零钱,请问有多少种兑换方法?
*
* @param total
* @param elements
* @return
* @author chenchanghan
*/
public static int getDivideWays(int total,int[] elements){
if(elements!=null && elements.length>=3){
int count = 0 ;
if(elements.length == 3){
count += getDivideWays(total,elements[0],elements[1],elements[2]);
}else{
int large = elements[0];
int[] subElements = new int[elements.length-1];
System.array(elements, 1, subElements, 0, subElements.length);
for (int i = 0, size = total / large; i <= size; i++) {
int restTotal = total - i * large;
if (restTotal != 0) {
count += getDivideWays(restTotal, subElements);
} else {
count++;
}
}
}
return count ;
}else{
throw new IllegalArgumentException();
}
}

‘肆’ 2014楂樿冩暟瀛﹂:鍗栭瀷镄勶纴涓鍙岄瀷杩涗环30鍏幂敥鍗20鍏冿纴椤惧㈡潵涔伴瀷缁欎简寮50锛岀帇甯埚倕娌¢浂阍憋纴浜庢槸镓

浠庡瓧闱涓婄湅镀忔槸浜忎简90鍏冿纴鍏跺疄鏄阌欑殑锛岃鏂囧瓧娣锋穯浜嗭纴姝g‘绛旀堟槸60鍏冦傚叾瀹为兘涓岖敤铡讳簤杈60銆90鍏冿纴浠婂ぉ鎴戝氨鎸夋爣鍑嗙殑浜ゆ槗娴佺▼锛堜粠杩涜揣鍒版渶钖庡埄娑︾泩浜忥级缁椤ぇ瀹惰︾粏瑙i喷姝g‘镄勭瓟妗堬纴锲捐В濡备笅锛

热点内容
诺基亚密码忘了打什么电话 发布:2024-09-17 03:27:09 浏览:555
树深度优先算法 发布:2024-09-17 03:26:58 浏览:472
跳转页源码 发布:2024-09-17 03:13:05 浏览:543
html文件上传表单 发布:2024-09-17 03:08:02 浏览:784
聊天软件编程 发布:2024-09-17 03:00:07 浏览:726
linuxoracle安装路径 发布:2024-09-17 01:57:29 浏览:688
两个安卓手机照片怎么同步 发布:2024-09-17 01:51:53 浏览:207
cf编译后没有黑框跳出来 发布:2024-09-17 01:46:54 浏览:249
安卓怎么禁用应用读取列表 发布:2024-09-17 01:46:45 浏览:524
win10设密码在哪里 发布:2024-09-17 01:33:32 浏览:662