先打廣告,所有的算法作業我都會把代碼更新在我的GitHub上
滑技工廠——Homework-AlgorithmAnalysis
希望大家能打個小⭐⭐,謝謝
依舊是算法作業,這次是整數劃分問題。
在網上找了半天,大多都是求整數劃分有多少種,很少求詳細的劃分情況。
有也都是C語言版的,並找不到Java的。
於是我便想出一個Java的(其實差不多,只是如果有人需要Java版的方便搜到)
問題
說明一下問題,什麼是整數劃分?
n=m1+m2+…+mi; (其中mi爲正整數,並且1 <= mi <= n),則{m1,m2,…,mi}爲n的一個劃分。
如果{m1,m2,…,mi}中的最大值不超過m,即max(m1,m2,…,mi)<=m,則稱它屬於n的一個m劃分。這裏我們記n的m劃分的個數爲f(n,m);
舉個例子,當n=6時我們可以獲得以下這幾種劃分:
6=6
6=5+1
6=4+2
6=4+1+1
6=3+3
6=3+2+1
6=3+1+1+1
6=2+2+2
6=2+2+1+1
6=2+1+1+1+1
6=1+1+1+1+1+1
分析
這樣子也非常容易看出來,n的整數劃分是建立在n-1的基礎上的,所以是可以有遞推公式的。
單個參數我們很難找到遞推式,於是我們增加一個參數m,表示一個劃分結果中的最大加數。
根據n和m的關係,考慮以下幾種情況:
-
當n=1時,不論m的值爲多少(m>0),只有一種劃分即{1};
-
當m=1時,不論n的值爲多少,只有一種劃分即n個1,{1,1,1,…,1};
-
當n=m時,根據劃分中是否包含n,可以分爲兩種情況:
(a)劃分中包含n的情況,只有一個即{n};
(b)劃分中不包含n的情況,這時劃分中最大的數字也一定比n小,即n的所有(n-1)劃分。 因此 q(n,n) =1 + q(n,n-1);
-
當n<m時,由於劃分中不可能出現負數,因此就相當於q(n,n);
-
但n>m時,根據劃分中是否包含最大值m,可以分爲兩種情況:
(a)劃分中包含m的情況,即{m, {x1,x2,…xi}}, 其中{x1,x2,… xi} 的和爲n-m,因此這情況下爲q(n-m,m)
(b)劃分中不包含m的情況,則劃分中所有值都比m小,即n的(m-1)劃分,個數爲q(n,m-1);
因此 q(n, m) = q(n-m, m)+q(n,m-1);
代碼
求整數劃分種數
/*
* @Title division
* @Description 整數劃分方法
* @author 滑技工廠
* @Date 2020/3/8
* @param [n, m] n->要劃分的整數 m->最大加數
* @return int 輸出有多少種劃分
* @throws
*/
public static int division(int n, int m) {
if (n < 1 || m < 1)
return 0;
if (n == 1 || m == 1)
return 1;
//最大加數如果大於n 則另最大加數爲n
if (n < m)
return division(n, n);
//m=n,則從
if (n == m)
return division(n, m - 1) + 1;
return division(n, m - 1) + division(n - m, m);
}
求具體劃分情況
static int mark[] = new int[100];//記錄分解情況
static int n;
/*
* @Title divide
* @Description 輸出劃分
* @author 滑技工廠
* @Date 2020/3/8
* @param [now, k, pre]
* @return void
* @throws
*/
public static void divide(int now, int k, int pre) {
int i;
//數組長度大於n就返回
if (now > n) return;
if (now == n) {
System.out.printf("%d=", n);
for (i = 0; i < k - 1; i++) {
System.out.printf("%d+", mark[i]);
}
System.out.printf("%d\n", mark[i]);
} else {
for (i = pre; i > 0; i--) {
if (i <= pre) {
mark[k] = i;
now += i;
divide(now, k + 1, i);
now -= i;
}
}
}
}
以6爲例,得到的結果爲
完結撒花✿,贈人玫瑰。