若将一张面值为 100 元的人民币等值换成100张面值为10 元、5 元、1 元和 0.5 元的零钞,要求每种零钞不少于1张。请编程实现,输出每一种可能的兑换组合

备考复习数据结构时偶然看到这道题,闲的没事做了一下,想到有两种解,或许有更优解有待研究

方法一:穷举法

这个方法小儿科,将所有组合列举出来,判断是否满徐条件,如果满足条件就输出,不满足就继续穷举,直到遍历完所有组合。这个方法由于要遍历所有组合,所以时间复杂度为O(n³),可谓是相当的大。

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main(void)
{
	uint16_t number10 = 1;	// 10元的张数
	uint16_t number5 = 1;	// 5元的张数
	uint16_t number1 = 1;	// 1元的张数
	uint16_t number05 = 1;	// 0.5元的张数
	uint16_t number_of_programme = 0;

	for (number10 = 1; number10 <= 100 - 3; number10++)
	{
		for (number5 = 1; number5 <= 100 - number10; number5++)
		{
			for (number1 = 0; number1 <= 100 - number10 - number5; number1++)
			{
				number05 = 100 - number10 - number1 - number5;
				if (10 * number10 + 5 * number5 + 1 * number1 + 0.5 * number05 == 100 && (number10 * number5 * number1 * number05 > 0))
				{
					number_of_programme++;	// 方案数量 +1
					printf("第%"PRIu16"种方案:%"PRIu16"张10元钞票、%"PRIu16"张5元钞票、%"PRIu16"张1元钞票、%"PRIu16"张0.5元钞票\n", number_of_programme, number10, number5, number1, number05);
				}
			}
		}
	}

	return 0;
}

方法二:瞎编的方法

该题目有两个关键条件:总面额为100元、总张数为100张,如果我们确保一个条件始终满足的前提下去判断另一个条件,时间复杂度或许会少许多,金额需要用到四个面值分别乘以对应的张数并加起来,而总张数只需要将四个面值的张数加起来即可,显然判断总张数更容易些,所以我们可以在确保总面额为100的前提下根据某些循环条件判断并输出总面额为100的组合。

然后我们发现如果将一种面值的零钱等价替换成另一种面值的零钱,确保金额不变的前提下,总张数就会发生改变,譬如将1张10元替换成10张1元,总张数就会增加9。所以我们可以将一个面值的零钱尽可能的多,其他三个面值的零钱尽可能的少,并且总金额为100,然后将这个面值的零钱逐一替换成其他面值的零钱,从而实现总张数的变化。

但是我们发现,由十元等价替换成其他面值的零钱,都会导致总张数的增加,这让我们不知道究竟该替换到哪个面值上去,所以只能从1元或者5元面值的零钱入手去替换成其他面值,这样如果总张数大于100,就替换成更高面值的零钱以减少总张数,反之替换成更小面值。但是我们又发现,5元替换成更高面值的只能替换给10元的了,但是10元的变化区间只有1~9,可见10元面值的零钱很少参与替换,所以我们就从1元开始入手。

上面听起来很绕口,简单来说就是:

  • 初始化时确保 10 元、5 元和 0.5 元达到最少值,1 元的数量尽可能多,即1张10元、1张5元、84张1元、2张0.5元。
  • 总张数过多时,将 5 张 1 元变为 1 张 5 元,总张数减少 4 张。
  • 总张数过少时,将 1 张 1 元变为 2 张 0.5 元,总张数增加 1 张。
  • 分为内循环和外循环,内循环将1元面值等价替换给5元和0.5元,外循环将1元面值等价替换给10元,循环判断条件都是1元面值张数不足时跳出循环

这样时间复杂度就时O(n²)且无限接近于O(n)(因为外循环总共就循环9次),下面放代码:

#include <stdio.h>

#define ALL 100		// 总张数

int main(void)
{
	const double C10 = 10.0;		// 10元金额
	const double C5 = 5.0;		// 5元金额
	const double C1 = 1.0;		// 1元金额
	const double C05 = 0.5;		// 0.5元金额
	int number10 = 1;	// 10元的张数最小值
	int number5 = 1;	// 5元的张数最小值
	int number05 = 2;	// 0.5元的张数最小值
	int number1 = (int)(ALL - C10 * number10 - C5 * number5 - C05 * number05);	// 1元的张数初始化

	int start_number = number1;	// 用于每次循环,1元张数的初始化
	int programme = 0;	// 用于记录方案数量

	// 外循环用于分配10元
	while (number1 >= 1)
	{
		// 内循环用于分配5元和0.5元
		while (number1 >= 1)
		{
			// 如果总张数大于100,则将1元面值零钱等价分配给5元面值零钱
			if ((number10 + number5 + number1 + number05) > 100)
			{
				number1 -= 5;
				number5 += 1;
				continue;
			}
			// 如果总张数大于100,则将1元面值零钱等价分配给0.5元面值零钱
			else if ((number10 + number5 + number1 + number05) < 100)
			{
				number1 -= 1;
				number05 += 2;
				continue;
			}
			// 如果总张数刚好等于100即满足条件,输出该组合
			if ((number10 + number5 + number1 + number05) == 100)
			{
				programme++;	// 项目数量 +1
				printf("第%d种方案:%d张10元、%d张5元、%d张1元、%d张0.5元\n", programme, number10, number5, number1, number05);
				// 将一张1元等价替换给0.5元,以改变number1的量,避免死循环
				number1 -= 1;
				number05 += 2;
			}
		}
		number5 = 1;	// 每次循环5元张数初始化为1
		number05 = 2;	// 每次循环0.5元张数初始化为0.5
		start_number -= 10;	// 内循环结束后,将10张1元合成1张10元,1元张数在
		number10 += 1;
		number1 = start_number;	// 初始化下次循环1元的张数,并用于外循环的判断
	}

	return 0;
}

今天分享就到这里了,鄙人不才,还在学习当中,欢迎批评指正!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值