整數的劃分問題是一個很經典的問題,它的變形也非常的多,總結了一下,大概有以下幾種變形:
1) 將 N 劃分為若干個正整數的和的劃分數
2) 將 N 劃分為若干個不同的正整數的和的劃分數
3) 將 N 劃分為不超過K 個正整數的和的劃分數
4) 將 N 劃分為不超過K 個不同正整數的和的劃分數
5) 將 N 劃分為最大數不超過K 的正整數的和的劃分數
6) 將 N 劃分為若干個連續正整數的和的劃分數
ohoh,怎麼這麼多@@
變形(1): 將 N 劃分為若干個正整數的和的劃分數。比如 6,可以這樣劃分:
6
5 + 1
4 + 2, 4 + 1 + 1
3 + 3, 3 + 2 + 1, 3 + 1 + 1 + 1
2 + 2 + 2, 2 + 2 + 1 + 1, 2 + 1 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1 + 1
所以答案應該是 11.
分析:
根據 n和 m的關係,考慮以下幾種情況:
(1 )當 n=1時,不論 m的值為多少( m>0),只有一種劃分即 {1};
(2) 當 m=1時,不論 n的值為多少,只有一種劃分即 n個 1, {1,1,1,...,1};
(3) 當 n=m時,根據劃分中是否包含 n,可以分為兩種情況:
( a)劃分中包含 n的情況,只有一個即 {n};
( b)劃分中不包含 n的情況,這時劃分中最大的數字也一定比 n小,即 n的所有 (n-1)劃分。
因此 f(n,n) =1 + f(n,n-1);
( 4)當 n<m時,由於劃分中不可能出現負數,因此就相當於 f(n,n);
( 5)但 n>m時,根據劃分中是否包含最大值 m,可以分為兩種情況:
( a)劃分中包含 m的情況,即 {m, {x1,x2,...xi}}, 其中 {x1,x2,... xi} 的和為 n-m,可能再次出現 m,因此是( n-m)的 m劃分,
因此這種劃分個數為 f(n-m, m);
( b)劃分中不包含 m的情況,則劃分中所有值都比 m小,即 n的 (m-1)劃分,個數為 f(n,m-1);
因此 f(n, m) = f(n-m, m)+f(n,m-1);
因此,設dp[n][m] 表示將 n 劃分為 最大數不超過 m 的劃分個數,有
dp[n][m] = 1 ( m = 1)
dp[n][m] = dp[n][m-1] + 1 ( m = n)
dp[n][m] = dp[n][n] ( m > n)
dp[n][m] = dp[n][m-1] + dp[n-m][m] ( m < n)
Code:
[cpp] view plain
copy
#include <cstdio>
#include <cstring>
#define MAXN 500
using namespace std;
int dp[MAXN][MAXN];
int main()
{
int i,j;
memset(dp,0,sizeof(dp));
for(i = 1;i < MAXN; ++i)
dp[i][1] = 1;
for(i = 1;i < MAXN; ++i)
for(j = 1;j < MAXN; ++j){
if(i > j)
dp[i][j] = dp[i][j-1] + dp[i-j][j];
else if(i == j)
dp[i][j] = dp[i][j-1] + 1;
else dp[i][j] = dp[i][i];
}
}
變形(2):將 N 劃分為若干個不同正整數的和的劃分數 TOJ.3511 Staircases
依然是DP,轉移方程為 a[k][n]=a[k+1][n]+a[k+1][n-k];其中a[k][n]表示將n劃分為不小於k種不同的數的劃分法。
初始化為 a[n][n]=1 ; a[k][n]=0 (k>n).
Code:
[cpp] view plain
copy
#include <cstdio>
#include <cstring>
#define MAXN 500
using namespace std;
int dp[MAXN][MAXN];
int main()
{
int i,j;
memset(dp,0,sizeof(dp));
for(i = 1;i < MAXN; ++i)
dp[i][i] = 1;
for(i = 2;i < MAXN; ++i)
for(j = 1;j < i; ++j)
dp[j][i] = 0;
for(i = 2;i < MAXN; ++i)
for(j = i-1;j >= 1; --j)
dp[j][i] = dp[j+1][i] + dp[j+1][i-j];
}
變形(3):將 N 劃分為不超過K 個正整數的和的劃分數
解法:dp[i][j] 表示將 i 劃分為 j 份的劃分數,則轉換為求 sigma( dp[N][ i ] ) (i <= K);
dp[i][j] 的轉移方程為 dp[i-1][j-1] + dp[i-j][j];
詳細見http://wurong81.spaces.live.com/blog/cns!5EB4A630986C6ECC!254.entry
Code:
[cpp] view plain
copy
#include <cstdio>
#include <cstring>
#define MAXN 500
using namespace std;
int dp[MAXN][MAXN];
int main()
{
int i,j;
// 求將某個數劃分為 m 份的劃分數
for(i = 1;i < MAXN; ++i)
for(j = 1;j <= (i<m?i:m); ++j)
dp[i][j] = dp[i-1][j-1] + dp[i-j][j];
}
變形(4):將 N 劃分為不超過K 個不同正整數的和的劃分數
解法:即為變形2的方程意義。a[k][n] 表示將 N 劃分為不超過K份不同的數的和的劃分數
變形(5):
變形(6): 這個最簡單了。N 可以寫為連續K(K >= 2)個正整數的和,則K <= sqrt(2*N);
令 i 從 K 到 2 迴圈,如果 i 為奇數 且 N %i == 0,則可分解為 i 個數,第一個數為 N/i - i/2;
如果 i 為偶數 且 N %i == i/2,則可分解為i 個數,第一個數為 (N-1)/i - i/2 + 1;