整数划分
问题描述: 将正整数 n n n 表示为若干个正整数的和, 问共有多少种表示? (不考虑排列问题)
上述描述是整数划分中最基本的描述, 整数划分是一类问题的总称. 正整数及其个数的不同约束可以得到不同的整数划分问题.
1. 最大值为 k k k 的正整数
该问题是最基本的约束条件, 对该约束分析如下:
设 f ( n , k ) f(n,k) f(n,k) 为问题的解 ( 即将正整数 n n n 划分为不大于 k ( k ≥ 1 ) k (k \geq 1) k(k≥1) 的正整数和的划分个数 ) , 则有下述等式成立
f ( n , k ) = { 0 , n < 0 且 k ≤ 0 ; 1 , n = 0 ; f ( n , n ) , k > n ; f ( n , k − 1 ) + f ( n − k , k ) , k ≤ n . f(n,k) = \begin{cases} 0, & \text{$n<0且k\le 0;$} \\[2ex] 1, & \text{$n=0;$} \\[2ex] f(n,n), & \text{$k>n;$} \\[2ex] f(n,k-1)+f(n-k,k), & \text{$k\leq n.$} \end{cases} f(n,k)=⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧0,1,f(n,n),f(n,k−1)+f(n−k,k),n<0且k≤0;n=0;k>n;k≤n.
根据上述结果, 得到如下 C++ 程序
int dp(int n, int k)
{
/*
求 n 的最大值为 k 的划分的个数
f(n, k) = f(n, k - 1) + f(n - k, k)
由上式可得: dp[n] += dp[n - k], (n >= k 且 dp[0] = 1)
*/
if (n <= 0 || k <= 0)
return 0;
int dp[128] = { 1 };
int realK = k <= n ? k : n;
int iN, jK;
for (jK = 1; jK <= realK; ++jK)
for (iN = jK; iN <= n; ++iN)
dp[iN] += dp[iN - jK];
return dp[n];
}
2. 若干个奇正整数
该问题可以转化为问题 5 进行解决.
3. 若干个不同的正整数
该问题是最基本的约束条件, 对该约束分析如下:
设 f ( n , k ) f(n,k) f(n,k) 为问题的解 ( 即将正整数 n n n 划分为不大于 k ( k ≥ 1 ) k (k \geq 1) k(k≥1) 的正整数和的划分个数 ) , 则有下述等式成立
f ( n , k ) = { 0 , n ≤ 0 或 k ≤ 0 ; 1 , n = 1 且 k > 0 ; f ( n , n ) , n > 1 且 k > n ; f ( n , k − 1 ) + f ( n − k , k − 1 ) , n > 1 且 k ≤ n . f(n,k) = \begin{cases} 0, & \text{$n\leq 0 或 k\leq 0;$} \\[2ex] 1, & \text{$n=1且k>0;$} \\[2ex] f(n,n), & \text{$n> 1 且 k>n;$} \\[2ex] f(n,k-1)+f(n-k,k - 1), & \text{$n>1 且 k\leq n.$} \end{cases} f(n,k)=⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧0,1,f(n,n),f(n,k−1)+f(n−k,k−1),n≤0或k≤0;n=1且k>0;n>1且k>n;n>1且k≤n.
根据上述结果, 得到如下 C++ 程序
int dpOfDifNum(int n, int k)
{
/*
求 n 的最大值为 k 的划分的个数
f(n, k) = f(n, k - 1) + f(n - k, k - 1)
由上式可得: dp[n] += dp[n - k], (n >= k 且 dp[0] = 1)
*/
if (n <= 0 || k <= 0)
return 0;
int dp[128] = { 1 };
int realK = k <= n ? k : n;
int iN, jK;
for (jK = 1; jK <= realK; ++jK)
for (iN = n; iN >= jK; --iN)
dp[iN] += dp[iN - jK];
return dp[n];
}
4. 若干个给定的正整数
该问题根据最基本的问题推导:
设给定的 m m m 个正整数为 k 1 , k 2 , . . . , k m k_1,k_2,...,k_m k1,k2,...,km ,记个数为 f ( n ) f(n) f(n) , 则有一下等式成立
f ( n , k i ) = { 0 , n < 0 ; 1 , n = 0 ; f ( n , n ) , n > 0 且 k i > n ; f ( n , k i − 1 ) + f ( n − k i , k i ) , n > 0 且 k i ≤ n . f(n,k_i) = \begin{cases} 0, & \text{$n<0;$} \\[2ex] 1, & \text{$n=0;$} \\[2ex] f(n,n), & \text{$n>0 且 k_i>n;$} \\[2ex] f(n,k_{i-1})+f(n-k_i,k_i), & \text{$n>0 且 k_i\leq n.$} \end{cases} f(n,ki)=⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧0,1,f(n,n),f(n,ki−1)+f(n−ki,ki),n<0;n=0;n>0且ki>n;n>0且ki≤n.
根据上述结果, 得到如下 C++ 程序
int dpMArr(vector<int>& mArr, int n)
{
/*
mArr 给定的正整数
n 需要求的数
*/
if (mArr.size() <= 0 || n <= 0)
return 0;
int dp[10240] = { 1 };
int m = mArr.size(),
realM = m <= n ? m : n;
int iN, jM;
for (jM = 0; jM < realM; ++jM)
for (iN = mArr[jM]; iN <= n; ++iN)
dp[iN] += dp[iN - mArr[jM]];
return dp[n];
}
5. m 个正整数之和
该问题是最基本的约束条件, 对该约束分析如下:
给问题等价于 ( n − m ) (n-m) (n−m) 的最大值为 m m m 的划分的个数。
设 f ( n , k ) f(n,k) f(n,k) 为问题 1 的解 ( 即将正整数 n n n 划分为不大于 k ( k ≥ 1 ) k (k \geq 1) k(k≥1) 的正整数和的划分个数 ) , 则该问题的解为 f ( n − m , m ) f(n-m,m) f(n−m,m) 。
根据上述分析, 得到如下 C++ 程序
int mSumOfNum(int n, int m)
{
return dp(n - m, m);
}