第1题:制作蛋糕
小A擅长制作香蕉蛋糕和巧克力蛋糕。制作一个香蕉蛋糕需要2个单位的香蕉,250个单位的面粉,75个单位的糖,100个单位的黄油。制作一个巧克力蛋糕需要75个单位的可可粉,200个单位的面粉,150个单位的糖,150个单位的黄油。一个香蕉蛋糕可以卖出400元,而一个巧克力蛋糕可以卖出450元。为了避免蛋糕变质,每种蛋糕至多只能制作100个。
现已知每种原料的数量,求小A至多可以卖出多少元的蛋糕。
时间限制:1000
内存限制:65536
输入
依次输入面粉、香蕉、糖、黄油、可可粉的数量,每种原料数量均为不超过100000的整数。
输出
输出一个整数,表示最多卖出的钱数。
样例输入
4000
6
2000
500
500
样例输出
1700
下面是使用 C 语言编写的解决方案,用于计算小A至多可以卖出的蛋糕的金额:
#include <stdio.h>int main() {int flour, banana, sugar, butter, cocoa;scanf("%d %d %d %d %d", &flour, &banana, &sugar, &butter, &cocoa);int maxBananaCake = banana / 2;int maxChocolateCake = cocoa / 75;int maxCakes = (maxBananaCake <= maxChocolateCake) ? maxBananaCake : maxChocolateCake;int totalAmount = 0;if (maxCakes > 100) {maxCakes = 100;}totalAmount += maxCakes * 400;banana -= maxCakes * 2;flour -= maxCakes * 250;sugar -= maxCakes * 75;butter -= maxCakes * 100;cocoa -= maxCakes * 75;int remainingCakes = 100 - maxCakes;while (remainingCakes > 0) {if (banana >= 2 && flour >= 250 && sugar >= 75 && butter >= 100) {totalAmount += 400;banana -= 2;flour -= 250;sugar -= 75;butter -= 100;remainingCakes--;} else if (cocoa >= 75 && flour >= 200 && sugar >= 150 && butter >= 150) {totalAmount += 450;cocoa -= 75;flour -= 200;sugar -= 150;butter -= 150;remainingCakes--;} else {break;}}printf("%d\n", totalAmount);return 0;
}
上述代码首先根据输入的原料数量,计算出最多可以制作的香蕉蛋糕数量 maxBananaCake
和巧克力蛋糕数量 maxChocolateCake
。然后,通过比较这两个数量,确定可以制作的蛋糕的最大数量 maxCakes
。
接下来,使用一个循环来制作蛋糕。首先,检查是否能制作 maxCakes
个香蕉蛋糕,如果可以,则将相应的原料数量减去,并增加对应的金额。然后,继续检查是否能制作剩余的蛋糕,优先制作香蕉蛋糕,然后再制作巧克力蛋糕。如果原料不足或超过剩余蛋糕的数量,循环终止。
最后,输出最多可以卖出的钱数 totalAmount
。
编译并运行上述代码,即可得到小A至多可以卖出的蛋糕的金额。
第2题:找和最接近但不超过K的两个元素
在一个长度为n(1 < n < 1000)的整数(0至1000之间)序列中,选出两个元素使得它们的和最接近但不超过K(0 <= K < 2000)。保证一定存在不超过K的两元素和。
时间限制:1000
内存限制:65536
输入
第一行输入一个整数n 第二行输入一个整数K 第三行输入序列,用空格分开
输出
最接近但不超过K的和
样例输入
4
7
1 2 2 8
样例输出
4
下面是使用 C 语言编写的解决方案,用于找出在给定整数序列中,两个元素的和最接近但不超过给定值 K:
#include <stdio.h>
#include <stdlib.h>int compare(const void *a, const void *b) {return *(int *)a - *(int *)b;
}int findClosestSum(int arr[], int n, int K) {int i = 0;int j = n - 1;int closestSum = 0;int diff = K;while (i < j) {int sum = arr[i] + arr[j];if (sum == K) {return sum;} else if (sum < K) {if (K - sum < diff) {diff = K - sum;closestSum = sum;}i++;} else {j--;}}return closestSum;
}int main() {int n;scanf("%d", &n); // 输入整数序列的长度int K;scanf("%d", &K); // 输入目标和 Kint *arr = malloc(n * sizeof(int));for (int i = 0; i < n; i++) {scanf("%d", &arr[i]); // 输入整数序列的元素}qsort(arr, n, sizeof(int), compare); // 对整数序列进行排序int result = findClosestSum(arr, n, K); // 找出最接近但不超过 K 的和printf("%d\n", result); // 输出结果free(arr);return 0;
}
上述代码首先定义了一个 compare
函数,用于在 qsort
函数中进行整数的比较,以实现升序排序。
在 main
函数中,首先读取输入的整数序列的长度和目标和 K。然后,动态分配内存来存储整数序列,并读取输入的整数序列的元素。接下来,使用 qsort
函数对整数序列进行升序排序。
最后,调用 findClosestSum
函数,传入排序后的整数序列、序列长度和目标和 K,找出最接近但不超过 K 的和。函数中使用双指针法来搜索最接近的和,通过比较当前和与目标和的差值来更新最接近和的值。
最后,输出结果。
编译并运行上述代码,即可得到在给定整数序列中,两个元素的和最接近但不超过给定值 K 的结果。
第3题:数根
树根可以通过把一个数的各个位上的数字加起来得到。如果得到的数是一位数,那么这个数就是数根。如果结果是两位数或者包括更多位的数字,那么再把这些数字加起来。如此进行下去,直到得到是一位数为止。比如,对于24来说,把2和4相加得到6,由于6是一位数,因此6是24的数根。再比如39,把3和9加起来得到12,由于12不是一位数,因此还得把1和2加起来,最后得到3,这是一个一位数,因此3是39的数根。
输入
一个正整数(小于10^1000)
输出
一个数字,即输入数字的数根
样例输入
24
样例输出
6
下面是使用 C 语言编写的解决方案,用于计算一个正整数的数根:
#include <stdio.h>int digitalRoot(int num) {if (num < 10) {return num; // 如果是一位数,直接返回}int sum = 0;while (num > 0) {sum += num % 10; // 取出最低位的数字并累加num /= 10; // 去掉最低位的数字}return digitalRoot(sum); // 递归计算数根
}int main() {char input[1001];scanf("%s", input); // 输入正整数int num = 0;for (int i = 0; input[i] != '\0'; i++) {num += input[i] - '0'; // 将字符转换为对应的数字并累加}int result = digitalRoot(num); // 计算数根printf("%d\n", result); // 输出结果return 0;
}
上述代码首先定义了一个 digitalRoot
函数,用于计算一个正整数的数根。如果输入的数小于 10,直接返回该数作为数根。否则,使用循环将每位数字相加,然后递归调用 digitalRoot
函数计算新的和的数根。
在 main
函数中,首先读取输入的正整数,并将其转换为对应的数字。使用一个循环来累加每位数字的值。然后,调用 digitalRoot
函数计算数根。
最后,输出结果。
编译并运行上述代码,即可得到输入正整数的数根。
第4题:迷信的病人
医院为了方便对患者进行建档和管理,引入了9位整数ID号来标识每个病人。最近医院入住了一个迷信的病人,他认为ID号的好坏直接决定了自己的命运。他对ID号x有如下要求:
(1)x的前三位数构成的整数是素数
(2)x的后三位数构成的整数是平方数(所谓平方数,是指它是某一个正整数的平方,e.g. 1,4,9,16…)
(3)x中不包含"13"
为了避免不必要的医患矛盾,医院须尽量满足他的需求。现给定正整数区间[m,n],请你判断存在几个满足病人需求的ID号。
时间限制:1000
内存限制:65536
输入
两个正整数m,n,以空格隔开。(999999999>=n>=m>=111111111)
输出
一个整数(满足要求的ID的个数)。
样例输入
157689476 157689687
样例输出
5
提示
存在5个满足要求的ID:157689484,157689529,157689576,157689625,157689676
下面是使用 C 语言编写的解决方案,用于判断在给定的正整数区间 [m, n]
中,存在多少个满足迷信病人需求的 ID 号:
#include <stdio.h>
#include <stdbool.h>
#include <math.h>bool isPrime(int num) {if (num < 2) {return false; // 0和1不是素数}for (int i = 2; i <= sqrt(num); i++) {if (num % i == 0) {return false; // 若存在除了1和本身的因子,则不是素数}}return true; // 是素数
}bool isSquare(int num) {int root = sqrt(num);return root * root == num; // 若平方根的平方等于原数,则是平方数
}bool contains13(int num) {while (num > 0) {if (num % 100 == 13) {return true; // 若出现13,则包含"13"}num /= 10;}return false; // 不包含"13"
}int countValidIDs(int m, int n) {int count = 0;for (int i = m; i <= n; i++) {int firstThreeDigits = i / 1000; // 取前三位数int lastThreeDigits = i % 1000; // 取后三位数if (isPrime(firstThreeDigits) && isSquare(lastThreeDigits) && !contains13(i)) {count++; // 满足迷信病人需求的 ID 号}}return count;
}int main() {int m, n;scanf("%d %d", &m, &n); // 输入正整数区间 [m, n]int result = countValidIDs(m, n); // 计算满足要求的 ID 号个数printf("%d\n", result); // 输出结果return 0;
}
上述代码定义了三个辅助函数:
-
isPrime
函数用于判断一个数是否为素数。若输入的数小于 2 或存在除了 1 和本身的因子,则不是素数,返回false
;否则,是素数,返回true
。 -
isSquare
函数用于判断一个数是否为平方数。通过计算平方根,若平方根的平方等于原数,则是平方数,返回true
;否则,不是平方数,返回false
。 -
contains13
函数用于判断一个数是否包含 “13”。通过不断除以 10 取余数的方式检查每两位数,若出现 “13” 则包含 “13”,返回true
;否则,不包含 “13”,返回false
。
在 countValidIDs
函数中,使用循环遍历给定的正整数区间 [m, n]
,对每个数字进行判断。首先,分别取出前三位数和后三位数。然后,使用辅助函数判断这两个部分是否满足迷信病人的要求:前三位数是素数、后三位数是平方数,并且整个数字不包含 “13”。如果满足要求,则计数器增加。最后,返回计数器的值。
在 main
函数中,首先读取输入的正整数区间 [m, n]
。然后,调用 countValidIDs
函数计算满足要求的 ID 号的个数。最后,输出结果。
编译并运行上述代码,即可得到在给定的正整数区间中满足迷信病人需求的 ID 号的个数。
第5题:算24
给出4个小于10个正整数,你可以使用加减乘除4种运算以及括号把这4个数连接起来得到一个表达式。现在的问题是,是否存在一种方式使得得到的表达式的结果等于24。 这里加减乘除以及括号的运算结果和运算的优先级跟我们平常的定义一致(这里的除法定义是实数除法)。 比如,对于5,5,5,1,我们知道5 * (5 – 1 / 5) = 24,因此可以得到24。又比如,对于1,1,4,2,我们怎么都不能得到24。
时间限制:6000
内存限制:65536
输入
输入数据包括多行,每行给出一组测试数据,包括4个小于10个正整数。最后一组测试数据中包括4个0,表示输入的结束,这组数据不用处理。
输出
对于每一组测试数据,输出一行,如果可以得到24,输出“YES”;否则,输出“NO”。
样例输入
5 5 5 1
1 1 4 2
0 0 0 0
样例输出
YES
NO
解决这个问题的一种常见方法是使用回溯法(Backtracking)。回溯法是一种递归的算法,通过尝试所有可能的组合来解决问题。
以下是解题的一般思路:
(1)定义一个递归函数,该函数接受四个参数:当前处理的数字列表、当前处理的运算结果、剩余可用的数字个数、目标结果。
(2)如果剩余可用的数字个数为0,检查当前处理的运算结果是否等于目标结果。如果是,返回 true;否则,返回 false。
(3)对于每个剩余的数字,尝试将其与运算结果进行加、减、乘、除四种运算,并递归调用函数处理剩余的数字。
(4)在递归调用前,保存当前运算结果,然后对其进行相应的运算操作。
(5)在递归调用后,恢复之前保存的运算结果,以便尝试其他运算。
(6)如果找到了满足条件的组合,返回 true;否则,返回 false。
下面是使用回溯法解决该问题的示例代码:
#include <stdio.h>
#include <stdbool.h>#define TARGET_RESULT 24
#define EPSILON 1e-6bool is24Possible(int nums[], int used[], double result, int count) {if (count == 0) {if (fabs(result - TARGET_RESULT) < EPSILON) {return true; // 当前结果等于24} else {return false; // 当前结果不等于24}}for (int i = 0; i < 4; i++) {if (!used[i]) {used[i] = 1;if (is24Possible(nums, used, result + nums[i], count - 1) ||is24Possible(nums, used, result - nums[i], count - 1) ||is24Possible(nums, used, result * nums[i], count - 1) ||is24Possible(nums, used, result / nums[i], count - 1)) {return true; // 找到满足条件的组合}used[i] = 0;}}return false; // 无法得到24
}bool is24PossibleWrapper(int nums[]) {int used[4] = {0}; // 数字使用情况的标记数组return is24Possible(nums, used, 0, 4);
}int main() {int nums[4];while (scanf("%d %d %d %d", &nums[0], &nums[1], &nums[2], &nums[3]) == 4) {if (nums[0] == 0 && nums[1] == 0 && nums[2] == 0 && nums[3] == 0) {break; // 输入结束}if (is24PossibleWrapper(nums)) {printf("YES\n"); // 可以得到24} else {printf("NO\n"); // 无法得到24}}return 0;
}
在上述代码中,我们定义了 is24Possible
函数来实现回溯法。函数中,我们首先检查剩余可用的数字个数是否为0,如果是,则检查当前运算结果是否等于目标结果。如果是,返回 true
;否则,返回 false
。
然后,我们使用一个循环来尝试每个剩余的数字。对于每个数字,我们标记其为已使用,并尝试将其与运算结果进行加、减、乘、除四种运算。然后,递归调用 is24Possible
处理剩余的数字。在递归调用前,我们保存当前运算结果,并在递归调用后恢复之前保存的运算结果,以便尝试其他运算。
在 main
函数中,我们使用循环读取输入的四个数字,直到遇到四个数字都为0的情况,表示输入结束。对于每组测试数据,我们调用 is24PossibleWrapper
函数来判断是否可以得到24,并根据结果输出 “YES” 或 “NO”。
这种回溯法的解决方法会尝试所有可能的组合,因此在最坏情况下,时间复杂度是指数级的。但是,由于数据规模较小(四个数字小于10),该方法仍然可行。如果数据规模较大,可能需要使用其他更高效的算法来解决。