1.问题提出
整数s的拆分是把s分成为某些指定正整数之和,拆分式中不允许零数重复,且不记零数的次序。试求s共有多少个不同的拆分式?展示出s的所有这些拆分式。
2.递归设计
注意到拆分与式中各零数的排列顺序无关,我们考虑从1~m这m个数中取k(k<m)个数的所有组合结果入手:设comb(int m, int k)为从1~m这m个数中取k个数的所有组合结果。
当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1个数的组合。这就把从m个数中取k个数的组合问题转化为从m-1个数中取k-1数的组合问题。
设置数组a[]存放救出的组合数字,约定函数将确定的k个数字组合的第一个数字放在a[k]中,当一个组合求出后,才将a[]的一个组合输出。第一个数可以是m, m-1, ..., k,函数将确定组合的第一个数字放入数组后,有两种选择:还未确定组合的其余元素时,继续递归确定组合的其余元素;已确定组合的全部元素时,输出这个组合。
对于给定的和数s与最大零数m,首先计算拆分式中零数的最少个数wmin与零数的最多个数wmax,显然,拆分式中零数的个数k取在区间[wmin,wmax]中。
建立三参数m、k、s的递归函数comb(m,k,s),当选取的第一个数i选定时,其后的数字是从余下的i-1个数中取k-1个数的组合。对所选取的k个数,求其和t并与和数s进行比较:若t=s,即找到一个拆分式,进行打印输出,并设置变量n统计拆分式的个数。
3.代码
// 整数的拆分
#include <stdio.h>
#define MAXN 100
// 数组a[]存放求出的组合数字
// n统计拆分的种数
int a[MAXN], t, n = 0;
void comb(int m, int k, int s)
{
int i, j;
for (i = m; i >= k; i--) {
a[k] = i;
if (k == 1) {
for (t = 0, j = a[0]; j > 0; j--)
t += a[j];
if (t == s) {
n++;
printf("%d=", s); // 满足条件时输出
for (j = a[0]; j > 1; j--)
printf("%2d+", a[j]);
printf("%2d/n", a[1]);
}
}
else
comb(i - 1, k - 1, s);
}
}
int main(void)
{
int ms, ss, i, h, k, wmin, wmax;
printf("请输入和数,最大零数:");
scanf("%d%d", &ss, &ms);
for (h = 0, i = 1; i <= ms; i++) {
h += i;
if (h > ss) {
wmax = i - 1;
break;
}
}
// 输入的最大零数太小,程序返回
if (i > ms) {
printf("输入的最大零数太小!/n");
return 0;
}
for (h = 0, i = 1; i <= ms; i++) {
h += ms - i + 1;
if (h > ss) {
wmin = i - 1;
break;
}
}
for (k = wmin; k <= wmax; k++) {
a[0] = k;
comb(ms, k, ss);
}
// 输出拆分种数
printf("n = %d/n", n);
return 0;
}
二、可以重复的所有情况
1.递推计算分划种数
⑴递推关系的确定
设n的“最大零数不超过m” 的分划式个数为q(n, m),这里m<=n,则
q(n,n)=1+q(n, n-1)
等式右边的“1”表示n等于n本身;q(n,n-1)表示n的所有其他分划,即最大零数不超过n-1的分划。
q(n,m)=q(n,m-1)+q(n-m,m) (1<m<n)
其中q(n,m-1)表示零数中不包含m的分划式数目;q(n-m,m)表示零数中包含m的分划数目,因为如果确定了一个分划的零数中包含m,则剩下的部分就是对于n-m进行不超过m的分划。
注意:如果n-m<m时,取q(n-m,m)=q(n-m,n-m),初始条件:q(n,0)=0, q(1,m)=1
2.程序实现
// 整数分划递推计数
#include <stdio.h>
#include <math.h>
int main()
{
int s, m, n;
long q[121][121];
printf("请输入整数s:"); // 输入分划的整数
scanf("%d", &s);
for (m = 1; m <= s; m++) { // 确定初始条件
q[m][0] = 0;
q[1][m] = 1;
}
for (n = 2; n <= s; n++) {
for (m = 1; m <= n -1; m++) {
if (n - m < m)
q[n - m][m] = q[n -m][n - m];
q[n][m] = q[n][m - 1] + q[n - m][m]; // 实施递推
}
q[n][n] = q[n][n - 1] + 1; // 加上n=n这一个分划式
}
printf("整数%d的分划种数:%ld/n", s, q[s][s]);
return 0;
}