1、将n划分成若干正整数之和的划分数:
设dp[i][j]为将i划分为不大于j的划分数
(1) 当i<j 时,i不能划分为大于i的数,所以dp[i][j]=dp[i][i];
(2) 当i=j 时,若划分中含有j只有一种情况,若划分中不含j相当于将i划分为不大于j-1的划分数。此时dp[i][j]=1+dp[i][j-1]。
(3) 当i>j 时,可以根据划分中是否含有j分为两种情况。若划分中含有j,划分方案数为dp[i-j][j];若划分数中不含j,相当于将i划分为不大于j-1的划分数,为dp[i][j-1]。所以当i>j时dp[i][j]=dp[i-j][j]+dp[i][j-1];
dp[n][n]为所求。
2、将n划分成k个正整数之和的划分数:
设dp[i][j]为将i划分为j个整数的划分数。
(1) i<j为不可能出现的情况,dp[i][j]=0;
(2) 若i=j,有一种情况:i可以划分为i个1之和,dp[i][j]=1;
(3) 若i>j,可以根据划分数中是否含有1分为两类:
第一类: n 份中不包含 1 的分法,为保证每份都 >= 2,可以先拿出 k 个 1 分
到每一份,然后再把剩下的 n- k 分成 k 份即可,分法有: dp[n-k][k]
第二类: n 份中至少有一份为 1 的分法,可以先那出一个 1 作为单独的1份,剩
下的 n- 1 再分成 k- 1 份即可,分法有:dp[n-1][k-1]
dp[n][k]为所求。
3、将n划分成最大数不超过k的划分数:
通过1的方法 dp[n][k]为所求。
4、将n划分成若干个奇正整数之和的划分数:
设dp[i][j] 是将i划分成不大于j 的奇正整数的划分数。
(1)当i < j 的时候,i 不能划分成大于i的数且只能划分为奇数,如果j是奇数,dp[i][j]=dp[i][i],如果j是偶数,dp[i][j]=dp[i][i - 1]。
(2)当i = j 的时候,若划分中含有j只有一种情况,若划分中不含j相当于将i划分为不大于j - 2的划分数, 因为偶数不能取,所以是j - 2。此时dp[i][j]=1+dp[i][j - 2]。
(3)当i < j 时,可以根据划分中是否含有j分为两种情况。若划分中含有j,相当于把i - j划分为不大于j的划分数,为dp[i-j][j];若划分中不含j,相当于将i划分为不大于j - 2的划分数,为dp[i][j - 2]。所以dp[i][j]=dp[i-j][j]+dp[i][j-2];
dp[n][n]或者dp[n][n - 1]为所求。
5、将n划分成若干不同整数之和的划分数:
设dp[i][j]为将i划分为不超过j的不同整数的划分数。
(1) 当i<j时,i不能划分为大于i的数,所以dp[i][j]=dp[i][i];
(2) 当i=j时,若划分中含有j只有一种情况,若划分中不含j相当于将i划分为不大于j-1的划分数。此时dp[i][j]=1+dp[i][j-1]
(3) 当i>j时,可以根据划分中是否含有j分为两种情况。若划分中含有j,则其余的划分中最大只能是j-1(与1不同的地方!!),方案数为dp[i-j][j-1];若划分中不含j,相当于将i划分为不大于j-1的划分数,为dp[i][j-1]。所以当i>j时dp[i][j]=dp[i-j][j-1]+dp[i][j-1];
dp[n][n]为所求。
#include <stdio.h>
int dp1[100][100];//将n划分成若干正整数之和的划分数or将n划分成最大数不超过k的划分数
int dp2[100][100];//将n划分成k个正整数之和的划分数
int dp4[100][100];//将n划分成若干个奇正整数之和的划分数
int dp5[100][100];//将n划分成若干不同整数之和的划分数
void one()
{//n划分成若干正整数之和的划分数or将n划分成最大数不超过k的划分数
将n划分成若干不同整数之和的划分数
int i, j;
for(i = 1; i < 100; i++)
{//i划分成不大于1的划分数和1划分成不大于i的划分数为1
dp1[i][1] = dp1[1][i] = 1;
dp5[1][i] = dp5[1][i] = 1;
}
for(i = 2; i < 100; i++)
{
for(j = 2; j < 100; j++)
{
if(i == j)
{
dp1[i][j] = dp1[i][j - 1] + 1;
dp5[i][j] = dp5[i][j - 1] + 1;
}
else if(i < j)
{
dp1[i][j] = dp1[i][i];
dp5[i][j] = dp5[i][i];
}
else
{
dp1[i][j] = dp1[i][j - 1] + dp1[i - j][j];
dp5[i][j] = dp5[i][j - 1] + dp5[i - j][j - 1];
}
}
}
}
void two()
{//将n划分成k个正整数之和的划分数
int i, j;
for(i = 0; i < 100; i++)
dp2[i][i] = 1;//将i划分成i个整数的划分数只有一种
for(i = 2; i < 100; i++)
{
for(j = 1; j < i; j++)
dp2[i][j] = dp2[i - j][j] + dp2[i - 1][j - 1];
}
}
void three()
{//将n划分成若干个奇正整数之和的划分数
int i, j;
for(i = 0; i < 100; i ++)
//将i划分为不大于1的正奇数的划分数和将1划分成不大于i的正奇数的划分数为1
dp4[i][1] = dp4[1][i] = 1;
for(i = 1; i < 100; i++)
{
for(j = 3; j < 100; j += 2)
{//j += 2
if(i < j)
{
if(i % 2)
dp4[i][j] = dp4[i][i];
else
dp4[i][j] = dp4[i][i - 1];
}
else//i == j和i > j的情况合并
dp4[i][j] = dp4[i - j][j] + dp4[i][j - 2];
}
}
}
int main (void)
{
int n, k;
one();
two();
three();
while(scanf("%d %d", &n, &k) != EOF)
{
printf("%d\n", dp1[n][n]);
printf("%d\n", dp2[n][k]);
printf("%d\n", dp1[n][k]);
if(n % 2)//注意判断
printf("%d\n", dp4[n][n]);
else
printf("%d\n", dp4[n][n - 1]);
printf("%d\n\n", dp5[n][n]);
}
return 0;
}
在网上还看到了将正整数划分成连续的正整数之和:
如15可以划分成4种连续整数相加的形式:
15
7 8
4 5 6
1 2 3 4 5
有些数可以写成连续N(>1)个自然数之和,比如14=2+3+4+5;有些不能,比如8.那么如何判断一个数是否可以写成连续N个自然数之和呢?一个数M若可以写成以a开头的连续n个自然数之和,则M=a+(a+1)+(a+2)+…+(a+n-1)=n*a+n*(n-1)/2,要求a不等于0,否则就是以a+1开头的连续n-1个整数了。
现在考虑一般的形式,设n为被划分的正整数,x为划分后最小的整数,i为划分的正整数的个数。有上述可知(i * x + i * (i - 1) / 2) = n, 满足条件的划分就是使x为正整数的所有情况。
如上例,当i = 1时,即划分成一个正整数时,x = 15,
当i = 2时, x = 7。
当i = 3时,x = 4, 当i = 4时,4/9,不是正整数,因此,15不可能划分成4个正整数相加。
当i = 5时,x = 1。
这里还有一个问题,这个i的最大值是多少?不过有一点可以肯定,它一定比n小。我们可以做一个假设,
假设n可以拆成最小值为1的划分,如上例中的1 2 3 4 5。这是n的最大数目的划分。如果不满足这个假设,
那么 i 一定比这个划分中的正整数个数小。因此可以得到这样一个公式i * (i - 1) / 2 <= n,即当i满足
这个公式时n才可能被划分。
代码如下:
void split(int n) {
int i, j, te, x, xlen;
for (i = 1, xlen = 0; (te = i * (i - 1) / 2) < n; i++) {
x = n - te;
if (x % i == 0) {
x /= i;
printf("%d", x);
for (j = 1; j < i; j++) {
printf("%d ", x + j);
}
printf("\n");
xlen++;
}
}
printf("%d\n", xlen);
}
参考: 点击打开链接