将一面额为10元倍数的整钱(面额为大于等于50,并小于等于100)换成1元、2元和5元的零钱组合(每种面值都要有)。输入要换的面额(如10元),输出所有可能的换法数量(如2种)。
1.双层for循环解法 - 复杂度O(n*2)
下面展示一些 for循环
解法。
分析如下
因为每种纸币都不可以为空 ,意味着我们可以先控制5元纸币的数量为最大数量,然后找出2元的最大数量,再次基础上,将两者分别作为循环变量以此递减,由于零钱组合只有5元2元和1元,所以只要5元数量和2元数量定下来,1元纸币的数量也已经定了下来。由此可以得出以下结果。
注意
:我们在控制了5元数量后,在控制2元数量的时候,我们需要注意总金额为10的倍数,在减去5元的倍数后,剩下的一定是整10和几十5,这样我们需要分开讨论:如果是整10,需要保证1元纸币不可以为零张,这时就要让剩余钱数/2 - 1,确保一元纸币张数 不为零;如果是几十 5,这时剩余钱数/2一定留有余数,一元纸币数量一定不为零,此时分类完毕。由于1元纸币数量在5元数量和2元数量定下来后已经确定所以可以不用管,直接计算种类数量即可。
检验
:我们可以根据20元的每种组合来检验以上说法:即依次减少5元纸币的数量,便会发现2元纸币有两种情况。
#include <stdio.h>
int main()
{
int Themoney = 0, maxFive = 0, maxTwo = 0, numbers = 0; // 定义为总面额、可兑换最多的5元个数、2元个数、总的种类数
scanf("%d", &Themoney);
maxFive = Themoney / 5 - 1;
for (maxFive ; maxFive > 0; maxFive --) // 依次减少5元纸币的个数但不少于1
{
// 如果减去5元的总面额剩下的钱为2的倍数,则2元纸币的个数需要在除以2的基础上 - 1,以此确保1元纸币个数不为零
if ((Themoney - maxFive * 5) % 2 == 0)
{
maxTwo = (Themoney - maxFive * 5) / 2 - 1;
}
else
{
maxTwo = (Themoney - maxFive * 5) / 2;
}
for (; maxTwo > 0; maxTwo--) // 依次减少2元纸币的个数但不少于1
{
numbers++; // 1元纸币个数在5元和2元纸币个数定下后已经定了,此时可以直接进行换法数量+1
}
}
printf("%d", numbers); // 输出总的换法
return 0;
}
2.单层for循环解法 - 复杂度O(n)
分析如下
既然我们已经知道每种纸币都必须有,我们可以先把每种纸币都兑换一张,以此来确保每种面值都要有的题目要求,然后再将剩余纸币进行分配,这是剩余纸币没有不能为零的限制,在剩余纸币可以兑换的情况下,我们可以将5元纸币、2元纸币数量为0,全部兑换一元纸币,我们仍然可以参考第一种 的两层for循环方法写出种类数。如下:
#include <stdio.h>
int main()
{
int Themoney = 0, surplusThemoney = 0, maxFive = 0, maxTwo = 0, numbers = 0;
scanf("%d", &Themoney);
surplusThemoney = Themoney - 8;
for (maxFive = surplusThemoney / 5;maxFive >= 0;maxFive--)
{
maxTwo = (surplusThemoney - maxFive * 5) / 2;
for (maxTwo;maxTwo >= 0;maxTwo--)
{
numbers++;
}
}
printf("%d", numbers);
return 0;
}
此时仍然是双层for循环,但是不急,我们会发现 在5元纸币即外层循环的执行的某个条件下,5元纸币数量一定,此时只需要确定2元纸币数量,此时我们仍然可以参考双层 for循环的分析:在整10的总钱数减去8后剩余钱数一定为2或者几十2,再减去0、5、几十5或者几十(即5元纸币所有可能的取值)的情况下,我们接下来就是从剩余金额中找出能够兑换多少张2元纸币(可以为零张),这时可以转化为从数0到剩余金额W之间(包括0和W)有多少个偶数,此时可以直接使用公式W/2+1求出2元纸币的种类(2元数量定下后,1元也默认定下了),此时便求出了外层循环某一条件下的种类数,此时我们只需将每层的种类数循环相加即可。
#include <stdio.h>
int main()
{
int themoney = 0, surplusThemoney = 0,numbers = 0;
scanf("%d", &themoney);
surplusThemoney = themoney - 8;
for (surplusThemoney; surplusThemoney > 0; surplusThemoney -= 5) numbers += surplusThemoney / 2 + 1;
printf("%d", numbers);
return 0;
}
3.数列公式解法- 复杂度O(1)
刚才经过努力我们消除了内层循环,此次我们来消除外层循环,经过观察我们发现方法二,,每次都是执行同一个公式,此时我们 可以使用我们高中学过的数列求和公式,可能听不懂但是举个例子你就知道了。
我们列出总金额W - 8后的所有公式
(12 - 0) / 2 + 1
(12 - 5) / 2 + 1
(12 - 10) / 2 + 1
这三个相加后我们发现(12 * 3 - 5 * 3) / 2 + 1 * 3
数字12就是surplusMoney剩余钱数
第一个和第三个数字3就是5元纸币的种类数(W - 8) / 5 + 1
,即5元纸币的总种类数
第二个数字3其实是0 + 1 + 2得出,其实就是从零到(W - 8)/5的总和
,即数列求和
这样子我们把求和公式的每一项都表示了出来,但是还是需要注意我们第二种解法的公式中消除了一部分代数,此时我们举例30元
(22 - 0) / 2 + 1
(22 - 5) / 2 + 1
(22 - 10) / 2 +1
(22 - 15) / 2 + 1
(22 - 20) / 2 + 1
上面5项的和我们会发现与刚刚找到的数列求和公式不同,比如(22 - 5) / 2 + 1 = 17 / 2 + 1,这里面的17除以2以后其实是舍弃了一个1的,但是我们的求和公式却并没有消除,这时我们需要减去这个未舍弃量的和/2,观察发现每隔2次会未舍弃一个1(累积一个1),即 每隔4次累积一个2,除以2以后会使总种类数多1,我们从总种类数中减去多出来的即可,即减去 ((W -8)/2 + 1) / 4。
此时便得出完善的求和公式:
(surplusMoney * (sruplussMoney / 5 + 1) - 5 * (( sruplussMoney / 5 ) * ( sruplussMoney / 5 + 1) / 2) ) / 2 + (sruplussMoney / 5 + 1) - ( sruplussMoney / 5 + 1) / 4
即:(剩余钱数 x 总循环数 - 总递减数(即数列求和数) * 5) / 2 + 总循环数 - 总累积数/4
将求和公式代入其中便可得出如下代码求解:
#include <stdio.h>
int main()
{
int Themoney, surplusMoney, sumSequence, maxFive, numbers;
scanf("%d", &Themoney);
surplusMoney = Themoney - 8;
maxFive = surplusMoney / 5;
sumSequence = ((maxFive + 1) * maxFive) / 2;
numbers = ((surplusMoney * (maxFive + 1)) - sumSequence * 5) / 2 + (maxFive + 1) - (maxFive + 1) / 4;
printf("%d", numbers);
return 0;
}
我们完全可以将变量定义和main函数的定义全部简化以此来达到装逼的效果:
#include <stdio.h>
int Themoney;
void main()
{
scanf("%d",&Themoney);
printf("%d", ((((Themoney - 8) * (((Themoney - 8) / 5) + 1)) - ((((Themoney - 8) / 5) + 1) * ((Themoney - 8) / 5)) / 2 * 5) / 2 + (((Themoney - 8) / 5) + 1) - (((Themoney - 8) / 5) + 1) / 4));
}
4.递归解法
觉得简单的可以参考我朋友的递归解法
以下放上他的链接,可详细学习
链接: 使用递归函数解决方法
5.输出种类数或者每个种类
这里只放上30的结果,其他结果已经过验证均正确。
如果是想要输出所有的组合项,我们只需要使用第一种方法的for循环进行输出即可。后面几种都是对第一种的简化并没有在意所有种类的过程。
#include <stdio.h>
int main()
{
int Themoney = 0, maxFive = 0, maxTwo = 0; // 定义为总面额、可兑换最多的5元个数、2元个数、总的种类数
scanf("%d", &Themoney);
maxFive = Themoney / 5 - 1;
for (maxFive ; maxFive > 0; maxFive --) // 依次减少5元纸币的个数但不少于1
{
// 如果减去5元的总面额剩下的钱为2的倍数,则2元纸币的个数需要在除以2的基础上 - 1,以此确保1元纸币个数不为零
if ((Themoney - maxFive * 5) % 2 == 0)
{
maxTwo = (Themoney - maxFive * 5) / 2 - 1;
}
else
{
maxTwo = (Themoney - maxFive * 5) / 2;
}
for (; maxTwo > 0; maxTwo--) // 依次减少2元纸币的个数但不少于1
{
printf("%d %d %d\n",maxFive,maxTwo,(Themoney - maxFive * 5 - maxTwo * 2));
}
}
return 0;
}
结果如下:
以上是我对这题的所有解法,谢谢观看。