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;
}