整數的劃分(遞歸法, 打表搜索, 動態規劃法)

遞歸法:

直接舉個例子什麼是整數的劃分。
如有整數 5
接下來對 5 經行劃分

5 + 0
4 + 1,
3 + 2
3 + 1 + 1,
2 + 2 + 1
2 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1

7 種 劃分方法。

這裏直接將用遞歸解決此問題的思路,
通過看上面的對 5 的劃分可以看出, 每種劃分中必定有一個最大數(可以有多個)。
我們可以將那個最大的數 成爲 m, 而 5 是待劃分的數, 稱之爲 n
接下來將數可以分爲倆個部分, m 和 n - mn - m就是除了最大的數其餘的數的和了)。

接下來對n ,m,和 n - m經行安排和分析。

  1. n == m 時, 就是最大的數就等於 n 本身, 那麼顯然就只有一種劃分方法就是 n + 0(除非你可以找出第二種,否則不要反駁)。
  2. n < m 時,就是最大的數大於了待劃分的數, 那麼顯然時不存在的,因爲不存在負數的劃分(如果存在負數,那麼也就不用存在這個問題了,答案必定時無窮種劃分 )。
  3. n > m 時, 這也是一般的情況,那麼就將數分成兩個部分{m, 和 n - m},然後分別對兩邊經行劃分,比如(如果此時劃分成 3 + 2 ,那麼 n - m = 2m = 3, 你可以發現 2 也是可以經行劃分的, 那麼也就不難理解爲啥要n - m經行向下遞推了 )。
  4. n == 1 時, 那麼就只有一種劃分。
  5. m == 1 時, 也只有一種劃分。

結合以上情況經行分析:
n == m 時 因爲出現了一種劃分, 所以要 + 1然後遞推下去 (n, m - 1)
n < m 時,那麼最大的數應該是 n 所以是 (n, n)
n == 1 或者 m == 1時, 就 + 1, 並且結束,因爲 1 不可以再經行劃分了。
n > m 時, 經行遞推 (n, m -1) + (n - m, m);

接下來將分析換算成代碼。

int fun(int n, int m){
   if(n == 1 || m == 1){
   	return 1;
   }
   if(n == m){
   	return fun(n, m - 1) + 1;
   }
   if(n < m){
   // 這裏其實應該時fun(n,n);
   // 但是n == m 時就會調用fun(n, m - 1); ,這裏可以直接略過這部。
   	return fun(n , n - 1) + 1;
   }
   return fun(n, m - 1) + fun(n - m, m);
}

基於以上代碼進行打表, 以增加搜索速度, 實際上就是動態規劃了. 但寫法還是舒服很多.
速度不亞於動態規劃的.

long num[10000][10000] = {0};
long fun(int n, int m){
	if(n <= 0 || m <= 0) return 0;
	if(n == 1 || m == 1) return 1;// 1種劃分
	
	if(num[n][m]) return num[n][m]; 
	
	long ans = 0;
	if(n <= m){
		ans += fun(n, n - 1) + 1;
	} else{
		ans += fun(n, m - 1);
		ans += fun(n - m, m);
	}
	
	num[n][m] = ans;
	return ans;
} 



動態規劃法:

思想和上面一樣,動態規劃主要是記錄了分化的結果,極大的減少劃分次數
一般要把遞歸代碼改寫成動態規劃就要 逆序 的經行迭代或者搜索
那麼如何記錄結果呢?
比如 5 ,4經行劃分時就需要對5, 31, 1經行劃分。
如果在之前就對5, 3經行了劃分呢?那麼能不能記下來呢?
對於整數的劃分,當然是最大值越小越好劃分,比如5, 1 就只有一種劃分。

那麼, 如果一開始就從 5,1 然後 5, 2 然後 5,3 這樣經行劃分,那麼就可以省掉大部分的劃分。
由此提高效率。

java代碼:

// 動態規劃
	static int dp(int n) {
		int result = 0;
		int map[][] = new int[n + 1][n + 1];
		int t = 0;
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= i; j++) {
				if(i == j) {// 比如 3, 3. 最大數爲3是一種劃分
					// 3, 3 = 3, 2 + 1
					map[i][j] = map[i][j - 1] + 1;
				}else if(1 == j) {// 這裏i == 1 可以省略
					map[i][j] = 1;
				} else {
					t = i - j;
					// 如果i - j < j 那麼就等同於i - j, i - j這種情況, 因爲最大值不可能大於待分解值
					if(t < j) {
						map[i][j] = map[i][j - 1] + map[t][t];
					}else {
						map[i][j] = map[i][j - 1] + map[t][j];
					}
				}
			}
		}
		result = map[n][n];
		return result;
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章