算法训练 数的划分  

时间限制:1.0s   内存限制:256.0MB

   

问题描述

  将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
  例如:n=7,k=3,下面三种分法被认为是相同的。
  1,1,5; 1,5,1; 5,1,1;
  问有多少种不同的分法。

输入格式

  n,k

输出格式

  一个整数,即不同的分法

样例输入

7 3

样例输出

4 {四种分法为:1,1,5;1,2,4;1,3,3;2,2,3;}

数据规模和约定

  6<n<=20,2<=k<=6

 

思路

将n划分为k个大于零的数的结果 = 划分结果中至少有一个1**+**划分结果中不存在1
论证1:划分结果中至少有一个1 = 将n-1分成k-1份的结果
理由:一定存在1,那么我就先划分出一个1,剩下的值无论怎么划分,最终结果中一定存在至少一个1
论证2:划分结果中不存在1 = 将n-k划分为k份的结果
理由:既然一定不存在1,则说明划分结果中的数一定都大于等于2,所以对于每一个数都减去1,那么剩下的结果一定是n-k划分成k份的结果

本文参考自  https://blog.csdn.net/dllgdxlxl/article/details/79438098

                    https://blog.csdn.net/qq_38786209/article/details/80111311 

 

代码实现

//动规(递推) 
//f[i][j]表示整数i划分为j份的分法 
//至少存在一个1(存在):f[n-1][k-1] ;不包含1:f[n-k][k]; 
#include<iostream>
#include<cstring>
using namespace std;
main(){
	int n,k;
	cin >> n >> k;
	int f[n+1][k+1];
	memset(f,0,sizeof(f));
	f[1][1]=1;
	for(int i=2;i<=n;i++)
		for(int j=1;j<=min(i,k);j++)
		    f[i][j]=f[i-1][j-1]+f[i-j][j];
	cout << f[n][k] << endl;
	return 0;
} 
 
//动规(递归)
#include<iostream>
using namespace std;
int n,k;
int f(int a,int b){
	if(a==0||b==0||b>a)
	   return 0;
	if(a==b||b==1||a==b+1)
	   return 1;
	return f(a-1,b-1)+f(a-b,b);
}
main(){
	cin >> n >> k;
	cout << f(n,k) << endl;
	return 0;
}