洛谷P2089烤鸡-C语言
题目背景
猪猪 Hanke 得到了一只鸡。
题目描述
猪猪 Hanke 特别喜欢吃烤鸡(本是同畜牲,相煎何太急!)Hanke 吃鸡很特别,为什么特别呢?因为他有 10 10 10 种配料(芥末、孜然等),每种配料可以放 1 1 1 到 3 3 3 克,任意烤鸡的美味程度为所有配料质量之和。
现在, Hanke 想要知道,如果给你一个美味程度 n n n ,请输出这 10 10 10 种配料的所有搭配方案。
输入格式
一个正整数 n n n,表示美味程度。
输出格式
第一行,方案总数。
第二行至结束, 10 10 10 个数,表示每种配料所放的质量,按字典序排列。
如果没有符合要求的方法,就只要在第一行输出一个 0 0 0。
样例
样例输入
11
样例输出
10
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 2 1 1
1 1 1 1 1 1 2 1 1 1
1 1 1 1 1 2 1 1 1 1
1 1 1 1 2 1 1 1 1 1
1 1 1 2 1 1 1 1 1 1
1 1 2 1 1 1 1 1 1 1
1 2 1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1 1 1
提示
对于 100 % 100\% 100% 的数据, n ≤ 5000 n \leq 5000 n≤5000。
问题分析
这个问题可以看作一个多重背包问题,但由于配料种类固定且数量较少(只有10种),我们可以用回溯法(递归)来高效解决。
解决思路
1.组合生成
1.有10种配料,每种配料的取值范围是1到3克。
2.问题可以看作尝试所有可能的组合,找出总和等于 n 的组合。
2.回溯法(递归)
1.用一个递归函数来尝试为每一个配料分配质量。
2.每次递归时,依次尝试1、2、3克并递归到下一个配料。
3.维护当前的配料总和,若当前总和超过了 n 则回溯(剪枝)。
4.若已经分配了10个配料且总和正好为 n,记录这个组合。
3.排序与输出
1.每找到一个符合条件的组合,就存储在一个列表中。
2.最终将所有组合按照字典序排序并输出。
代码实现
下面是详细的代码实现,包含注释以帮助理解每个步骤的功能:
#include <stdio.h>
#include <stdlib.h> // 包含 qsort 所需的库
#define MAXN 30 // 定义符号常量表示最大美味程度3*10
#define MAX_COMBINATIONS 59049 // 定义符号常量表示最大可能的组合数 3^10
// 全局变量声明
int combination[10]; // 用于存储当前组合的配料质量
int result[MAX_COMBINATIONS][10]; // 存储所有符合要求的组合
int result_index = 0; // 当前存储到 result 数组的索引,表示找到的组合数量
// 递归函数用于寻找所有符合条件的组合
// 参数:
// index - 当前处理的配料索引(第几个配料)
// current_sum - 当前组合的总重量
// target - 目标美味程度
void find_combinations(int index, int current_sum, int target) {
// 如果已经分配了10种配料
if (index == 10) {
// 检查当前组合的总质量是否等于目标美味程度
if (current_sum == target) {
// 将当前组合存储到结果数组
for (int i = 0; i < 10; i++) {
result[result_index][i] = combination[i];
}
result_index++; // 增加结果索引
}
return; // 结束当前递归
}
// 尝试为当前配料分配1到3克的重量
for (int i = 1; i <= 3; i++) {
// 如果当前总重量加上 i 小于等于目标美味程度
if (current_sum + i <= target) {
combination[index] = i; // 设置当前配料的重量
find_combinations(index + 1, current_sum + i, target); // 递归处理下一个配料
}
}
}
// 比较函数用于 qsort 排序
// 按字典序比较两个组合
// 参数:
// a - 指向第一个数组
// b - 指向第二个数组
int compare(const void *a, const void *b) {
const int *arr1 = (const int *)a;
const int *arr2 = (const int *)b;
// 按字典序逐个比较两个数组的每个元素
for (int i = 0; i < 10; i++) {
if (arr1[i] != arr2[i]) {
return arr1[i] - arr2[i]; // 返回差值决定顺序
}
}
return 0; // 如果所有元素都相等,则返回0
}
int main() {
int n; // 目标美味程度
scanf("%d", &n); // 从输入读取目标美味程度
// 寻找所有符合条件的配料组合
find_combinations(0, 0, n);
// 如果没有找到任何组合
if (result_index == 0) {
printf("0\n"); // 输出0
} else {
printf("%d\n", result_index); // 输出组合总数
// 对结果数组进行字典序排序
qsort(result, result_index, sizeof(result[0]), compare);
// 输出每一个组合
for (int i = 0; i < result_index; i++) {
for (int j = 0; j < 10; j++) {
printf("%d ", result[i][j]); // 输出组合中的每个配料重量
}
printf("\n"); // 换行
}
}
return 0;
}
总结
1.递归与回溯:
1.通过递归函数逐步为每个配料分配1到3克的重量。
2.在递归过程中维护当前配料的总重量,若总重量超过目标美味程度 n 则停止当前路径的探索(回溯)。
3.当所有10种配料都分配完且总重量等于 n 时,记录当前组合。
2.组合记录:
每找到一个符合条件的组合,就将其存储在一个结果数组中。
3.字典序排序:
使用 qsort 函数对找到的所有组合进行字典序排序,以确保输出结果按顺序排列。
4.数组大小设计:
定义 result 数组大小为 [59049][10],其中59049是 3^10,表示所有可能的组合数。这样可以确保程序能处理所有配料组合情况。
5.剪枝优化:
在递归过程中,若当前配料总和已经超过目标美味程度 n,立即停止当前路径的探索,进行回溯,从而提高算法效率。