整数划分是将一个正整数n表示为一系列正整数之和的问题。顺序不同的划分视为相同的划分。这是一个经典的组合数学和计算机科学问题。
1. 问题定义
将正整数n表示为:
n = a₁ + a₂ + ... + aₖ (aᵢ ≥ 1, 1 ≤ i ≤ k)
其中划分的顺序不重要,即 3+2 和 2+3 被视为相同的划分。
2. 递推关系
设 p(n, m) 表示将整数n划分成最大加数不超过m的划分个数。
递推关系:
-
p(n, 1) = 1 (只有全1的划分)
-
p(1, m) = 1 (只有1本身)
-
p(n, m) = p(n, n) 当 m > n
-
p(n, m) = p(n, m-1) + p(n-m, m) 当 n > m ≥ 1
3. C语言实现
方法一:递归实现
cpp
#include <stdio.h>
// 递归计算整数划分
int partition_recursive(int n, int m) {
if (n < 1 || m < 1) return 0;
if (n == 1 || m == 1) return 1;
if (n < m) return partition_recursive(n, n);
if (n == m) return 1 + partition_recursive(n, m - 1);
return partition_recursive(n, m - 1) + partition_recursive(n - m, m);
}
int main() {
int n;
printf("请输入要划分的整数 n: ");
scanf("%d", &n);
printf("整数 %d 的划分数: %d\n", n, partition_recursive(n, n));
return 0;
}
方法二:动态规划
cpp
#include <stdio.h>
#include <stdlib.h>
// 动态规划计算整数划分
int partition_dp(int n) {
if (n <= 0) return 0;
int **dp = (int**)malloc((n + 1) * sizeof(int*));
for (int i = 0; i <= n; i++) {
dp[i] = (int*)malloc((n + 1) * sizeof(int));
}
// 初始化
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
if (i == 0 || j == 0) {
dp[i][j] = 0;
} else if (i == 1 || j == 1) {
dp[i][j] = 1;
} else if (i < j) {
dp[i][j] = dp[i][i];
} else if (i == j) {
dp[i][j] = 1 + dp[i][j - 1];
} else {
dp[i][j] = dp[i][j - 1] + dp[i - j][j];
}
}
}
int result = dp[n][n];
// 释放内存
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
free(dp);
return result;
}
int main() {
int n;
printf("请输入要划分的整数 n: ");
scanf("%d", &n);
printf("整数 %d 的划分数: %d\n", n, partition_dp(n));
return 0;
}
方法三:优化的动态规划(一维数组)
cpp
#include <stdio.h>
#include <stdlib.h>
// 优化的动态规划(使用一维数组)
int partition_dp_optimized(int n) {
if (n <= 0) return 0;
int *dp = (int*)malloc((n + 1) * sizeof(int));
// 初始化
dp[0] = 1; // 空划分算一种
for (int i = 1; i <= n; i++) {
dp[i] = 0;
}
// 动态规划计算
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
dp[j] += dp[j - i];
}
}
int result = dp[n];
free(dp);
return result;
}
int main() {
int n;
printf("请输入要划分的整数 n: ");
scanf("%d", &n);
printf("整数 %d 的划分数: %d\n", n, partition_dp_optimized(n));
return 0;
}
4. 输出所有划分
cpp
#include <stdio.h>
#include <stdlib.h>
// 输出所有整数划分
void print_partitions(int n, int max, int *arr, int index) {
if (n == 0) {
// 找到一个划分,输出
for (int i = 0; i < index; i++) {
printf("%d", arr[i]);
if (i < index - 1) printf(" + ");
}
printf("\n");
return;
}
for (int i = max; i >= 1; i--) {
if (n >= i) {
arr[index] = i;
print_partitions(n - i, i, arr, index + 1);
}
}
}
int main() {
int n;
printf("请输入要划分的整数 n: ");
scanf("%d", &n);
int *arr = (int*)malloc(n * sizeof(int));
printf("整数 %d 的所有划分:\n", n);
print_partitions(n, n, arr, 0);
free(arr);
return 0;
}
5. 计算并输出所有划分(带计数)
cpp
#include <stdio.h>
#include <stdlib.h>
int count = 0;
// 输出所有划分并计数
void print_and_count_partitions(int n, int max, int *arr, int index) {
if (n == 0) {
count++;
printf("%d: ", count);
for (int i = 0; i < index; i++) {
printf("%d", arr[i]);
if (i < index - 1) printf(" + ");
}
printf("\n");
return;
}
for (int i = max; i >= 1; i--) {
if (n >= i) {
arr[index] = i;
print_and_count_partitions(n - i, i, arr, index + 1);
}
}
}
int main() {
int n;
printf("请输入要划分的整数 n: ");
scanf("%d", &n);
int *arr = (int*)malloc(n * sizeof(int));
count = 0;
printf("整数 %d 的所有划分:\n", n);
print_and_count_partitions(n, n, arr, 0);
printf("总划分数: %d\n", count);
free(arr);
return 0;
}
6. 完整测试程序
cpp
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 递归方法
int partition_recursive(int n, int m) {
if (n < 1 || m < 1) return 0;
if (n == 1 || m == 1) return 1;
if (n < m) return partition_recursive(n, n);
if (n == m) return 1 + partition_recursive(n, m - 1);
return partition_recursive(n, m - 1) + partition_recursive(n - m, m);
}
// 动态规划方法
int partition_dp(int n) {
if (n <= 0) return 0;
int **dp = (int**)malloc((n + 1) * sizeof(int*));
for (int i = 0; i <= n; i++) {
dp[i] = (int*)malloc((n + 1) * sizeof(int));
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == 1 || j == 1) {
dp[i][j] = 1;
} else if (i < j) {
dp[i][j] = dp[i][i];
} else if (i == j) {
dp[i][j] = 1 + dp[i][j - 1];
} else {
dp[i][j] = dp[i][j - 1] + dp[i - j][j];
}
}
}
int result = dp[n][n];
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
free(dp);
return result;
}
// 优化的动态规划
int partition_dp_optimized(int n) {
if (n <= 0) return 0;
int *dp = (int*)malloc((n + 1) * sizeof(int));
dp[0] = 1;
for (int i = 1; i <= n; i++) {
dp[i] = 0;
}
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
dp[j] += dp[j - i];
}
}
int result = dp[n];
free(dp);
return result;
}
int main() {
int n;
printf("整数划分问题测试\n");
printf("请输入要划分的整数 n: ");
scanf("%d", &n);
if (n <= 0) {
printf("请输入正整数\n");
return 1;
}
printf("\n方法比较:\n");
// 测试递归方法(只适用于小n)
if (n <= 30) {
clock_t start = clock();
int result_rec = partition_recursive(n, n);
clock_t end = clock();
double time_rec = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("递归方法: %d (耗时: %.6f秒)\n", result_rec, time_rec);
} else {
printf("递归方法: n太大,跳过计算\n");
}
// 测试动态规划
clock_t start = clock();
int result_dp = partition_dp(n);
clock_t end = clock();
double time_dp = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("动态规划: %d (耗时: %.6f秒)\n", result_dp, time_dp);
// 测试优化的动态规划
start = clock();
int result_opt = partition_dp_optimized(n);
end = clock();
double time_opt = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("优化动态规划: %d (耗时: %.6f秒)\n", result_opt, time_opt);
return 0;
}
7. 编译和运行
bash 复制 下载
gcc integer_partition.c -o integer_partition ./integer_partition
8. 算法分析
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 递归 | O(2ⁿ) | O(n) | 小规模数据 (n < 30) |
| 动态规划 | O(n²) | O(n²) | 中等规模数据 |
| 优化动态规划 | O(n²) | O(n) | 推荐,适合大多数情况 |