对于上文所述的递推关系(注意这里是对j的i划分):
我们这时讨论j>i的情况,
可以假设有一个大小为i的集合,dp[i][j-i]是对j-i的i划分,dp[i][j]的状态就可以从前者转移而来,即前者划分所对应的大小为i集合所有元素+1之后得到的新集合,可以保证这个新集合所有的元素都>0;(也就可以说,原集合存在0并没有什么影响,即使原集合的大小比i小也无影响,可以向集合中添加0去增加个数)(这里指的集合内的元素允许重复)
举个例子:1+1+2这个划分
0 | 0 | 1 | dp[3][1] |
1 | 1 | 2 | dp[3][4] |
2 | 2 | 3 | dp[3][7] |
... | ... | ... | ... |
dp[3][j] |
最后一点,dp[i][j]表示的是不超过i次划分的划分方法数,所以还要加上dp[i-1][j](对j的从1到i-1次划分的划分方法数)
dp[i][j]=dp[i][j-i]+dp[i-1][j]
蒜头君特别喜欢数学。今天,蒜头君突发奇想:如果想要把一个正整数 nn 分解成不多于 kk 个正整数相加的形式,那么一共有多少种分解的方式呢?
蒜头君觉得这个问题实在是太难了,于是他想让你帮帮忙。
输入格式
共一行,包含两个整数 n(1 \leq n \leq 300)n(1≤n≤300) 和 k(1 \leq k \leq 300)k(1≤k≤300),含义如题意所示。
输出格式
一个数字,代表所求的方案数。
样例输入
5 3
样例输出
5
#include<iostream>
using namespace std;
long long dp[305][305];
int main()
{
int n,k;
cin >> n >> k;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k;j++)
{
if(i==1||j==1)
dp[i][j]=1; //对应被划分数字为1和划分块数为1的情况,同时也是为后面进行初始化
else if(i<j)
dp[i][j]=dp[i][i]; //对应划分的块数多于被划分数字所能被划分的最大块数情况
//dp[i][i]是i能被划分的最多次数,同时也是为后面进行初始化
else if(i>j)
dp[i][j]=dp[i-j][j]+dp[i][j-1]; //计数dp
else
dp[i][j]=1+dp[i][j-1]; //原理同计数dp,只是一种特殊情况的特判
//dp[i-j][j]=dp[0][j]而数组的这个位置并没有初始化过,所以用1代替
//dp[i][j]=dp[i-j][j]+dp[i][j-1];这么写也行
//不过要初始化dp[0][j]=1(j: 1 to k)
}
}
cout << dp[n][k];
return 0;
}