整數劃分 :
這道好題求:
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;
}