例题,将正整数拆分问题
将正整数 n 分解成3个正整数,如 6 = 1 + 2 + 3,排在后面的数必须大于等于前面的数,输出所有方案。上机操作:循环实现上机操作:递归实现
将正整数 n 分解成 小于等于m 个正整数之和,且排在后面的数必须 大于等于 前面的数,并输出所有方案。输入:4 3输出:1 1 21 32 24
对于问题1,我们可以使用循环处理,using 3 loops
for (int i = 1; i <= n; ++i)for (int j = i; j <= n; ++j)for (int k = j; k <= n; ++k)if (i + j + k == n) printf("%d=%d+%d+%d\n", n, i, j, k);
如果问题变成“将正整数n分解成4个正整数”,此时就需要4重循环。如果问题变成“分解成小于等于m个整数”,那么,就没办法使用loops去弄了,需要使用递归搜索。
下面,我们考虑问题2。(用搜索树,来表示)
我们将问题分层,第 i 层决定 ai。则为了进行第 i 层决策,我们需要记录三个状态变量:
- 表示后面所有正整数的和
- 表示前一层的正整数,以确保正整数递增
- 分解了几个数,确保我们最多输出 m 个正整数
// arr[]记录方案void dfs(int n, int i, int last) {if (n == 0) {//输出方案}if (i <= m) {for (int j = last; j <= n; j++) {arr[i] = j;dfs(n - j, i + 1, j); //剩余n-j, 分解了i+1个数,前一个数用的是j}}}dfs(n, 1, 1);
示例程序
// 将正整数n分解成 小于等于m个 正整数之和,且排在后面的数必须大于等于前面的数,并输出所有方案#include <bits/stdc++.h>using namespace std;int n, m;int a[110];void dfs(int left, int u, int prev){if (left == 0){for (int i = 1; i < u; i++) printf("%d ", a[i]);puts("");return ;}if (u > m) return ;for (int i = prev; i <= left; i++){a[u] = i;dfs(left - i, u + 1, i);}}int main(){cin >> n >> m;dfs(n, 1, 1); //剩余n可分配,当前第1位,可以从1开始用return 0;}
//把n恰好分成m个正整数的方案,问题1的递归实现#include <bits/stdc++.h>using namespace std;int n, m;int a[110];void dfs(int left, int u, int prev){if (left == 0){if (u == m + 1){for (int i = 1; i < u; i++) printf("%d ", a[i]);puts("");}return ;}if (u > m) return ;for (int i = prev; i <= left; i++){a[u] = i;dfs(left - i, u + 1, i);}}int main(){cin >> n >> m;dfs(n, 1, 1); //剩余n可分配,当前第1位,可以从1开始用return 0;}
//重新设计一个dfs函数,这回是带用了多少作为参数//把n恰好分成m个正整数的方案,问题1的递归实现#include <bits/stdc++.h>using namespace std;int n, m;int A[110];void dfs(int u, int sum, int a) //当前第u个,已经分配了sum,可以从a开始用{if (u == m + 1){if (sum == n){for (int i = 1; i <= m; i++) printf("%d ", A[i]);puts("");}return ;}for (int j = a; j <= n; j++){A[u] = j;dfs(u + 1, sum + j, j);}}int main(){cin >> n >> m;dfs(1, 0, 1);return 0;}
