备考复习数据结构时偶然看到这道题,闲的没事做了一下,想到有两种解,或许有更优解有待研究
方法一:穷举法
这个方法小儿科,将所有组合列举出来,判断是否满徐条件,如果满足条件就输出,不满足就继续穷举,直到遍历完所有组合。这个方法由于要遍历所有组合,所以时间复杂度为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;
}
今天分享就到这里了,鄙人不才,还在学习当中,欢迎批评指正!