1.数的划分
题目描述
将整数 nn 分成 kk 份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3n=7,k=3,下面三种分法被认为是相同的。
1,1,5;1,5,1;5,1,1;1,1,5;1,5,1;5,1,1;
问有多少种不同的分法。
输入描述
输入一行,22 个整数 n,k (6≤n≤200,2≤k≤6)n,k (6≤n≤200,2≤k≤6)。
输出描述
输出一个整数,即不同的分法。
输入输出样例
示例 1
输入
7 3
输出
4
递归解题思路
看到"整数分拆"问题,直接想到"递归"
递归的核心思想:将大问题分解为小问题,通过解决小问题来构建大问题的解。
递归的终止条件:
-
当
n
或m
为 0,或者n
小于m
时,分拆方式数量为 0。 -
当
m
为 1 或n
等于m
时,分拆方式数量为 1。
递归的分解逻辑:
-
不选1的情况 :将每个数字减去1,问题转化为
f(n - m, m)
。 -
选1的情况 :其中一个数字是1,问题转化为
f(n - 1, m - 1)
。
最终结果是两种情况的和:f(n - m, m) + f(n - 1, m - 1)
。
解题步骤模板:
public static int f(int n, int m) {
// 边界条件
if (n == 0 || m == 0 || n < m) {
return 0;
}
if (m == 1 || n == m) {
return 1;
}
// 递归逻辑
else {
return f(n - m, m) + f(n - 1, m - 1);
}
}
示例代码模板:
import java.util.Scanner;
public class Main {
// 递归函数,用于计算将整数 n 分成 m 份的方式数
public static int f(int n, int m) {
// 边界条件:当 n 或 m 为 0,或者 n 小于 m 时,分拆方式数量为 0
if (n == 0 || m == 0 || n < m) {
return 0;
}
// 当 m 为 1 或 n 等于 m 时,分拆方式数量为 1
if (m == 1 || n == m) {
return 1;
}
// 递归逻辑:
// 1. 不选1的情况:将每个数字减去1,问题转化为 f(n - m, m)
// 2. 选1的情况:其中一个数字是1,问题转化为 f(n - 1, m - 1)
// 总分拆方式数量为两者的和
else {
return f(n - m, m) + f(n - 1, m - 1);
}
}
// 主函数,程序入口
public static void main(String[] args) {
// 创建 Scanner 对象,用于读取用户输入
Scanner sc = new Scanner(System.in);
// 读取用户输入的整数 n
int n = sc.nextInt();
// 读取用户输入的整数 k
int k = sc.nextInt();
// 调用递归函数 f(n, k),计算分拆方式数量
// 输出结果
System.out.println(f(n, k));
}
}
思维导图:

训练方法:
-
理解递归思想:仔细阅读代码,确保理解递归的终止条件和递归逻辑。
-
手算小例子 :选择一个小的例子(如
n = 4
,m = 2
),手动计算递归调用的过程,然后用代码验证结果。 -
调试和优化:观察递归调用的深度和次数,尝试用记忆化技术优化代码,避免重复计算。
-
扩展应用:将代码逻辑应用到其他类似问题,如"将整数分成任意多份"的问题
2.数的计算
题目描述
输入一个自然数 n (n≤1000)n (n≤1000),我们对此自然数按照如下方法进行处理:
-
不作任何处理;
-
在它的左边加上一个自然数,但该自然数不能超过原数的一半;
-
加上数后,继续按此规则进行处理,直到不能再加自然数为止。
问总共可以产生多少个数。
输入描述
输入一个正整数 nn。
输出描述
输出一个整数,表示答案。
输入输出样例
示例 1
输入
6
输出
6
递归解题思路
看到"整数分拆"问题,直接想到"递归"
递归的核心思想:将大问题分解为小问题,通过解决小问题来构建大问题的解。
递归的终止条件:当 n == 1
时,分拆方式数量为 1。
递归的分解逻辑:通过遍历从1到n/2的数i,每次将当前数i分拆,并递归计算i的分拆方式。
解题步骤模板:
public static void f(int n) {
// 递归终止条件
if (n == 1) {
return;
}
// 遍历 i 从 1 到 n/2,计算分拆方式
for (int i = 1; i <= n / 2; i++) {
// 每次分拆时,增加结果计数
res++;
// 递归计算更小的整数 i 的分拆方式
f(i);
}
}
示例代码模板:
import java.util.Scanner;
public class Main {
// 全局变量,用于存储最终结果
static int res = 1;
// 递归函数,用于计算整数 n 的分拆方式数量
public static void f(int n) {
// 递归终止条件:当 n == 1 时,分拆方式数量为 1
if (n == 1) {
return;
}
// 遍历 i 从 1 到 n/2,计算分拆方式
for (int i = 1; i <= n / 2; i++) {
// 每次分拆时,增加结果计数
res++;
// 递归计算更小的整数 i 的分拆方式
f(i);
}
}
// 主函数,程序入口
public static void main(String[] args) {
// 创建 Scanner 对象,用于读取用户输入
Scanner sc = new Scanner(System.in);
// 读取用户输入的整数 n
int n = sc.nextInt();
// 调用递归函数 f(n),计算分拆方式数量
f(n);
// 输出最终结果
System.out.println(res);
}
}
思维导图:

训练方法:
-
理解递归思想:仔细阅读代码,确保理解递归的终止条件和递归逻辑。
-
手算小例子 :选择一个小的例子(如
n = 4
),手动计算递归调用的过程,然后用代码验证结果。 -
调试和优化:观察递归调用的深度和次数,尝试用记忆化技术优化代码,避免重复计算。
-
扩展应用:将代码逻辑应用到其他类似问题,如"将整数分成特定数量的份"或"将整数分成不相等的份"的问题。
自学蓝桥杯笔记,希望我们可以一起学习!
