划分数问题 有n个无区别的物品,将他们划分成不超过m组,求出划分方法数模M的余数。 输入:n,m,M = 4,3,10000 输出:4
特别的,当m=n时,叫做n的划分数。通俗的讲就是一个正整数n可以划分成多少个正整数的和?
我们可以这样定义:
dp[i][j] := 把j划分成个数不超过i的方案总数
为了不重复计算,我们把n的划分数归纳于一系列非负整数的集合ai, $$ \sum_{i=1}^ma_i = n $$ ai 的定义如上,列入4的3划分为{0,0,4},{0,1,3},{0,2,2},{1,1,2},方案一{0,0,4}的a1,a2,a3分别是0,0,4,依次类推。
可以看出,每个集合里面有m个数,但是不都全为正整数。
所以我们可以将问题分成两类,一个是{ai}里面不包括0,一个是里面存在0。对于不存在0,将4的3划分数{1,1,2}作为例子,可以发现当里面的数个减去一,就变成了{0,0,1},即1的3划分数,它们两个的个数是相同的。所以{ai-1}就对应了n-m的m划分就不难理解了,就是集合里的每个数减去一,一共减了m个。
dp[i][j] = dp[i][j-i]
对于存在0,就好比上面的{0,0,4},{0,1,3},{0,2,2},可以发现集合里面各去掉一个数就变成了{0,4},{1,3},{2,2},即4的2划分。它们的个数是一样的。即
dp[i][j] = dp[i-1][j]
总结:
1 2 3 4 5 6 ''' 由此可以发现 dp[3][4] = dp[3][1] + dp[2][4] 所以可以推出递推公式: dp[i][j] = dp[i][j-i] + dp[i-1][j] 边界条件:dp[0][0] = 1 '''
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ''' 有n个无区别的物品,将他们划分成不超过m组,求出划分方法数模M的余数 输入: n,m,M = 4,3,10000 输出: 4 ''' n,m,M = 4 ,3 ,10000 dp = [[0 for i in range (n+1 )] for j in range (m+1 )] def DP (): dp[0 ][0 ] = 1 for i in range (1 ,m+1 ): for j in range (0 ,n+1 ): if j-i >= 0 : dp[i][j] = (dp[i-1 ][j] + dp[i][j-i]) % M else : dp[i][j] = dp[i-1 ][j] print (dp[m][n]) DP()
同样的问题:n的划分数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ''' 一个正整数可以划分为多个正整数的和,比如n=3时: 3;1+2;1+1+1; 共有三种划分方法。 给出一个正整数,问有多少种划分方法。 数据规模和约定 n< =100 样例输入 3 样例输出 3 ''' n = int (input ()) dp = [[0 for i in range (n+1 )] for j in range (n+1 )] def DP (): dp[0 ][0 ] = 1 for i in range (1 ,n+1 ): for j in range (0 ,n+1 ): if j - i >= 0 : dp[i][j] = dp[i-1 ][j] + dp[i][j-i] else : dp[i][j] = dp[i-1 ][j] print (dp[n][n]) DP()
思维图: