数的划分🍉
将正数n分为k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1, 1, 5;
1, 5, 1;
5, 1, 1.
问有多少种不同的分法。
输入描述
输入一行。2个正数n, k,且 。
输出描述
输出一个正数,即不同的分法。
输入样例
7 3
输出样例
4
思路题解
这一题实际上是组合数学里面的经典问题:将i个小球放到j个盒子中,小球之间与盒子之间没有区别,并且最后的结果不允许空盒。我们首先定义一个二维数组dp[][],dp[i][j]表示将整数 i 划分为 j 份 的方案数。dp[i][j]的动态转移方程为 :
该如何理解这个式子呢?我们重点看这一句话:不允许空盒。
我们使用题目的输入案例作为一个具体的例子来解释:令,因此现在我们有3个盒子,我们必须先拿出 3个盒子,将 3个盒子分别放上一个小球,保证每个盒子不空,即现在每个盒子的值为1,那么现在
变成了
,即
,这不难理解:一共7个小球,代表着7个1,现在将其中3个小球放进三个盒子,所以只剩下了4个小球,变成了dp[4][3],即dp[i-j][j]问题。
那么接下来我们又该如何处理呢?我们可以将剩下的所有小球都放到一个盒子里面去(dp[i-j][1]),也可以分到两份中去(dp[i-j][2]) ,...,也可以分到j份中(dp[i-j][j]),而每一种分发都是不相同的,所以可以将每一种分法全部加起来,和即为dp[i][j]。
想必看到这里,大家都懂得了这个算法啦!不过这个式子还可以变得更加优美一点儿:求dp[i-1][j-1],具体优化方法如下:
从而最终的式子为:
AC code
n, k = map(int, input().split())
# 初始化一个二维数组,用于存储 f(n, m)
dp = [[0 for j in range(210)] for i in range(210)]
for i in range(1, n+1):
dp[i][1] = 1
dp[i][i] = 1
'''
(7 3)->(4 3)+(6 2)
'''
for i in range(3, n+1):
for j in range(2, k+1):
if i > j:
dp[i][j] = dp[i-j][j] + dp[i-1][j-1]
print(dp[n][k])