数的划分
题目描述
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;
1,5,1;
5,1,1;
问有多少种不同的分法。
输入
每个测试文件只包含一组测试数据,每组输入两个整数n和k(6<n<=200,2<=k<=6)。
输出
对于每组输入数据,输出一个整数,即不同的分法。
下面是对样例数据的说明:
样例中的四种分法是:
1,1,5
1,2,4
1,3,3
2,2,3
样例输入
7 3
样例输出
4
提示
思路:
感觉这是一道非常基础的dp题目。
基础的动态转移方程是:dp[i][j] = dp[i-j][1]+ dp[i-j][2]+……+ dp[i-j][j]
首先,如果拿到一个整数 i ,因为题目中要求每份不能为空,因此必须先拿出 j 个数位将 j 份分别放上1,此时剩下 i - j个数。那么剩下的数如何处理呢?可以将其全部分到一份当中(dp[i-j][1]),也可以分到两份中(dp[i-j][2]),...,也可以分到 j 份中(dp[i-j][j]),而每一种分法都是不相同的,所以可以将其全部加起来,和即为dp[i][j]。
不过这个式子看起来并不简洁,为了求得一个简洁的式子,我们再求一个dp[i-1][j-1],
得到了这个简洁的结果
直接理解这个式子,其实就是现在要求的等于之前的等于两种可能的相加结果
1、dp[i-j][j]就是已经全部将j分完了,那么这个加不加上都不会产生新的可能。
2、dp[i-1][j-1]是还差一份需要分,正好1份对应这个1。
#include<iostream> using namespace std; int main() { int n,k; cin >> n >> k; //全部初始化为0 int dp[201][7] = {0}; //对于dp[0][0]作特殊处理(为了后面的动态转移方程能够起作用) dp[0][0] = 1; for(int i = 1; i <= n; i++)//i的取值范围是1~n(不能超过给定的整数) { for(int j = 1; j <= k; j++)//j的范围是1~k(不能超过需要划分的份数) { //划分的分数要小于等于该数本身 if(j <= i) //动态转移方程 dp[i][j] = dp[i-j][j] + dp[i-1][j-1]; } } cout << dp[n][k] << endl; return 0; }