题目
总时间限制: 1000ms;内存限制: 65536kB
描述
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
输入
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
输出
对输入的每组数据M和N,用一行输出相应的K。
样例输入
1 7 3
样例输出
8
来源
lwx@POJ
思路
一种理解,构造一个划分函数q(n,m),n表示苹果的个数,m表示一个盘子中放置苹果的最大数量。将n个苹果进行划分,考虑在划分中最大加数为m,也就是限制某个盘子中最多的苹果数量是m,那么有两种情况,一种情况是确实存在这样一个盘子,其中苹果的数量是m,这时我们只需要考虑剩下的n-m个苹果进行划分,限制某个盘子中最多的苹果数量是m,也就是q(n-m,m);另一种情况是不存在其中苹果数量为m的盘子,也就是某个盘子中苹果的数量最多为m-1,问题转化为了q(n,m-1)。由此我们得到了这个问题的递推公式,或者说递归公式——。
显然,如果n=1或m=1,只有一个苹果,或者只有一个盘子,就只有一种方法,这是一个终止条件。如果盘子的限制放置数量m超过苹果的数量n,那么某个盘子中苹果数量最多为n,q(n,m)转化为了q(n,n)。如果盘子的限制放置数量m等于苹果的数量n,这时某一个盘子放n个苹果肯定是一种放法,且其他盘子没有苹果可放了,也可以限制一个盘子中最多放n-1个苹果,问题转化为q(n,n-1)。之所以这样讨论,也是因为可以避免q(n,m)的参数为0或负数的情况,方便统一描述问题。
但,这种理解下,盘子的数量并不限于m个?不过我们可以这样想,一开始我们没有用盘子放苹果,只是把苹果把放在了一列列的表格中,表格的行数限制为m,最后才把不同行的苹果分别收集起来,放到对应行代表的盘子中。不限制盘子的数量转而限制每个盘子中苹果的数量,似乎绕了一个弯路,不过最终得到的还是苹果不同分法的数量K。或许这也有助于我们理解整数划分的问题。
对划分函数q(n,m)的另一种理解是,n表示苹果的数量,m表示可以用来放置苹果的盘子的个数,允许有的盘子空着不放,q(n,m)即是求将n个苹果放到m个盘子中,有多少种不同的分法。这样的话,我们每次放苹果到盘子中,可以选择放,也可以选择不放。如果选择每个盘子都放苹果,那么每个盘子至少要放一个苹果,我们先认为这时每个盘子都放了1个苹果,还剩下n-m个苹果,有m个盘子可供放置,即需要求q(n-m,m);如果并非每个盘子都放苹果,那么至少要有1个盘子为空,也就是我们还剩下m-1个盘子可以用来放n个苹果,问题转化为了q(n,m-1)。所以,递推公式为:。
显然,如果n=1或m=1,只有一个苹果,或者只有一个盘子,就只有一种放置方法,这是一个终止条件。如果盘子的数量m多于苹果的数量n,那么实际上最多也只能用到n个盘子,问题变为q(n,n)。如果盘子的数量m等于苹果的数量n,一种简单的放法是每个盘子放1个苹果,正好放完;要么存在没有放苹果的盘子,也就是说至少有1个盘子没有放苹果,最多用了n-1个盘子来放苹果,问题转化为q(n,n-1)。
当然,从题目的描述来看,后一种理解更合适理解题意,不过实际上两种理解最终得到的结果是一样的,也可以转换为等价的操作,其本质就是整数划分问题。
Python参考代码
递归形式
def q(n,m): #n>0,m>0
if n==1 or m==1:
return 1
elif n<m:
return q(n,n)
elif n==m:
return q(n,n-1)+1
else: #n>m>1
return q(n,m-1)+q(n-m,m)
t=int(input())
M,N=map(int,input().split())
for i in range(t):
print(q(M,N))
动态规划形式(初始化的设置和边界情况需要仔细考虑)
def dp_q(n, m):
# 初始化 dp 数组为 0
dp = [[0 for _ in range(m+1)] for _ in range(n+1)]
# 边界条件
for i in range(1, n+1):
dp[i][1] = 1 # 将 i 划分为 1 个部分只有一种方式
dp[i][0] = 0 # 无法将 i 划分为 0 个部分
for j in range(1, m+1):
dp[1][j] = 1 # 将 1 划分为 j 个部分只有一种方式
# 处理 m > n 的情况
if m > n:
m = n
# 递推关系
for i in range(2, n+1):
for j in range(2, m+1):
if i <= j:
dp[i][j] = dp[i][i-1] + 1
else:
dp[i][j] = dp[i][j-1] + dp[i-j][j]
return dp[n][m]
# 输入处理
t = int(input())
for _ in range(t):
M, N = map(int, input().split())
print(dp_q(M, N))
参考链接
整数划分问题 - 菜菜籽的文章 - 知乎https://zhuanlan.zhihu.com/p/455145557
递归算法《M个苹果放入N个盘子》递归算法《M个苹果放入N个盘子》_把m个同样的苹果放在n个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分-CSDN博客