整数划分 :
这道好题求:
1. 将n划分成若干正整数之和的划分数。
2. 将n划分成k个正整数之和的划分数。
3. 将n划分成最大数不超过k的划分数。
4. 将n划分成若干奇正整数之和的划分数。
5. 将n划分成若干不同整数之和的划分数。
1.若划分的多个整数可以相同
设dp[i][j]为将i划分为不大于j的划分数
(1) 当i<j时,i不能划分为大于i的数,所以dp[i][j]=dp[i][i];
(2) 当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];
(3) 当i=j时,也分为两种情况:
若划分中含有j只有一种情况,
若划分中不含j相当于将i划分为不大于j-1的划分数。
此时dp[i][j]=1+dp[i][j-1]。
2.若划分的正整数必须不同
设dp[i][j]为将i划分为不超过j的不同整数的划分数
(1) 当i<j时,i不能划分为大于i的数,所以dp[i][j]=dp[i][i];
(2) 当i>j时,可以根据划分中是否含有j分为两种情况。
若划分中含有j,则其余的划分中最大只能是j-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];
(3) 当i=j时,两种情况:
若划分中含有j只有一种情况;
若划分中不含j相当于将i划分为不大于j-1的划分数。
此时dp[i][j]=1+dp[i][j-1]。
二 将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分为两类:
若划分数中含有1,可以使用“截边法”将j个划分分别截去一个1,
把问题转化为i-j的j-1个划分数,为dp[i-j][j-1];
若划分中不包含1,使用“截边法”将j个划分数的最下面一个数截去,
将为题转化为求i-j的j个划分数,为dp[i-j][j]。所以i>j时dp[i][j]=dp[i-j][j-1]+dp[i-j][j]。
三 将n划分为若干正奇数之和的划分数
设f[i][j]为将i划分为j个奇数之和的划分数,g[i][j]为将i划分为j个偶数之和的划分数。
使用截边法,将g[i][j]的j个划分都去掉1,可以得到f[i-j][j],所以
g[i][j] = f[i-j][j]。
f[i][j]中有包含1的划分方案和不包含1的划分方案。对于包含1的划分方案,可以将1的划分除去,转化为“将i-1划分为j-1个奇数之和的划分数”,即f[i-1][j-1];对于不包含1的划分方案,可以使用截边法对j个划分每一个都去掉一个1,转化为“将i-j划分为j个偶数之和的划分数”,即g[i-j][j]。
所以f[i][j]=f[i-1][j-1]+g[i-j][j]。
f[n][0]+f[n][1]+……+f[n][n]为将n划分为若干奇数的划分数,为问题4的答案。
另:
将整数划分成连续正整数之和:
如15可以划分成4种连续整数相加的形式:
15
7 8
4 5 6
1 2 3 4 5
首先考虑一般的形式,设n为被划分的正整数,x为划分后最小的整数,如果n有一种划分,那么
结果就是x,如果有两种划分,就是x和x 、x + 1, 如果有i种划分,就是
x ;
x 、x + 1 ;
x、 x + 1、 x + 2 、... ;
x、 x + 1、 x + 2、 ... 、 x + i - 1;
将每一个结果相加得到一个公式(i * x + i * (i - 1) / 2) = n,i为当前划分后相加的正整数个数。
满足条件的划分就是使x为正整数的所有情况。
如上例,
当i = 1时,x = 15;
当i = 2时, x = 7;
当i = 3时,x = 4;
当i = 4时,x = 4/9; (x不是正整数,因此,15不可能划分成4个正整数相加)
当i = 5时,x = 1;
当i = 6时,x < 1; (所以i最大为5)
好了,咱们看题
03:复杂的整数划分问题
-
描述
-
将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
正整数n 的这种表示称为正整数n 的划分。
输入
-
标准的输入包含若干组测试数据。每组测试数据是一行输入数据,包括两个整数N 和 K。
(0 < N <= 50, 0 < K <= N)
输出
-
对于每组测试数据,输出以下三行数据:
第一行: N划分成K个正整数之和的划分数目
第二行: N划分成若干个不同正整数之和的划分数目
第三行: N划分成若干个奇正整数之和的划分数目
样例输入
-
5 2
样例输出
-
2 3 3
提示
-
第一行: 4+1, 3+2,
第二行: 5,4+1,3+2
第三行: 5,1+1+3, 1+1+1+1+1+1
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <map>
using namespace std;
int main()
{
int n, k;
while( cin >> n >> k )
{
int num[n+1][n+1]; // 表示把n分成k个数的dp数组;
int num1[n+1][n+1]; // 表示把n分成不相等正整数的dp数组;
int g[55][55], f[55][55]; // g数组为偶数dp数组, f数组为奇数dp数组;
int i, j;
// 初始化;
for( i=0; i<=n; i++ )
{
for( j=0; j<=n; j++ )
{
num[i][j] = num1[i][j] = f[i][j] = g[i][j] = 0;
}
}
// 这就是上面说的其中两种。
for( i=1; i<=n; i++)
{
for( j=1; j<=n; j++)
{
if( i < j )
{
num[i][j] = 0;
num1[i][j] = num1[i][j-1];
}
if( i == j )
{
num[i][j] = 1;
num1[i][j] = 1 + num1[i][j-1];
}
if( i > j )
{
num[i][j] = num[i-1][j-1] + num[i-j][j];
num1[i][j] = num1[i-j][j-1] + num1[i][j-1];
}
}
}
// 这里是最晕的,只要记住就好了,搞懂也没多大意义;
f[0][0]=1; g[0][0]=1;
for( i=1; i<=n; i++ )
{
for( j=1; j<=i; j++)
{
g[i][j] = f[i-j][j];
f[i][j] = f[i-1][j-1] + g[i-j][j];
}
}
cout << num[n][k] <<endl;
cout << num1[n][n] <<endl;
int sum = 0;
for( i=0; i<=n; i++)
sum += f[n][i];
cout << sum <<endl;
}
return 0;
}