第一种:求 把n划分为最大值不超过m的 划分数
1) 若是划分多个整数可以存在相同的:
dp[n][m] 存的是n划分为最大值不超过m的划分个数;
dp[n][m] = dp[n][m-1]+dp[n-m][m];
dp[n][m]表示整数 n 的划分中,每个数不大于 m 的划分数。 则划分数可以分为两种情况:
a.划分中每个数都小于 m,相当于每个数不大于 m- 1, 故划分数为 dp[n][m-1],就这样横着从左到有依次类推,一直加着.
b.划分中有一个数为 m. 那就在 n中减去 m ,剩下的就相当于把 n-m 进行划分, 故划分数为 dp[n-m][m];
2).若是划分多个不同的整数:
dp[n][m] = dp[n][m-1] + dp[n-m][m-1];
dp[n][m] 表示整数n的划分中,每个数不大于m的划分数,则可以分为两种情况
a.划分中每个数都小于 m,相当于每个数不大于 m- 1, 故划分数为 dp[n][m-1],就这样横着从左到有依次类推,一直加着.
b.划分中有一个数为 m.在n中减去m,剩下相当对n-m进行划分,并且每一个数不大于m-1,
故划分数为 dp[n-m][m-1];
第二种:将n划分成k个数的划分法:
dp[n][k] = dp[n-k][k] + dp[n-1][k-1];
第一类: n 份中不包含 1 的分法,为保证每份都 >= 2,可以先拿出 k 个 1 分到每一份,然后再把剩下的 n- k 分成 k 份即可,
分法有: dp[n-k][k];也可这样解释:dp[n-k][k],n-k分为k份,因为这k份都是大于0,让着每一份都加上1,变为dp[n][k];
第二类: n 份中至少有一份为 1 的分法,可以先那出一个 1 作为单独的1份,剩下的 n- 1 再分成 k- 1 份即可,分法有:dp[n-1][k-1]
第三种:把n划分为不超过m份的划分数
设dp[i][j] 为把 j 划分为不超过 i 份的划分数;
dp[i][j] = dp[i][j-i] + dp[i-1][j]
dp[i][j-i] ,把 j-i 划分为不超过 i 份的 方法数(不够 i 份的用 0 补),让i份,每一份都加上1就转移到了dp[i][j]
而这个 j 划分的 i 份,每一份都是大于0的;
dp[i-1][j] 当 j 划分为i-1份时,再填个0,就变成了i份,就转移到了dp[i][j], 这是后可能会有人问那dp[i-2][j],填两个0,不就变成了i份吗,不就也转移到了dp[i][j]了吗,你想想,当你找dp[i-1][j]时,已经加上了dp[i-2][j] 的 方法数,若你这时再加dp[i-2][j] 中的方法数,不就加重复了吗,这就是dp,就是递推,一直累加;
下面是一道第三种类型的题:
蒜头君特别喜欢数学。今天,蒜头君突发奇想:如果想要把一个正整数 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
题意:求n划分为不超过k份的划分数;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
long long dp[400][400]; //dp[i][j] 当把j划分成不超过i份的划分数;
int main()
{
int i,j;
int n,m;
scanf("%d%d",&n,&m);
memset(dp[0],0,sizeof(dp[0]));
dp[0][0] = 1;
for(i = 1;i<=m;i++)
{
for(j = 0;j<=n;j++)
{
if(j-i>=0) dp[i][j] = dp[i-1][j] + dp[i][j-i];
else dp[i][j] = dp[i-1][j];
}
}
printf("%lld\n",dp[m][n]);
return 0;
}