整數劃分部分問題和演算法
一.基本劃分問題
問題描述:給定乙個正整數n,求將其劃分為m份的不同方法數。
問題分析:
(1) 首先,何謂不同方法總數?即兩個不同的劃分的元素不能完全相同,eg:10 = 1+2+3+4,是10的乙個劃分,那麼10 = 2+1+3+4也應該是10的乙個劃分,但是由於這兩個劃分的元素完全相同,所以實際只能算同一種劃分。
(2) 搞清楚不同方法數之後,那麼如果我們在劃分時只要按照不下降排列的方式進行劃分就不會有重複,這樣進行下去就可以得到最後的方法總數
演算法設計:
(1) 首先看原始的動態規劃演算法設計:考慮劃分過程中的最大數 k,令f[i][j][k]表示將整數j劃分成 i 份,而最後乙個數最大為 k 的劃分方案總數。那麼可見:該狀態(i, j, k)可由前面「i-1」份的「j-k」加上「乙份k」而來,因此當前狀態的方案總數就等於「j-k」劃分為i-1份的所有最大加數小於等於k的方案數至和。得到狀態轉移方程:f[i][j][k] = sum
dp演算法1: o(n^3*m)
set all f[i][j][k] = 0;
f[0][0][0] = 1;
for(i = 1; i <= m; i++)
for(j = i; j <= n; j++)
for(k = 1; k <= j; k++)
for(l = 0; l <= k; l++)
f[i][j][k] += f[i-1][j-k][l];
for(i = 1, cnt = 0; i <= n; i++) cnt += f[m][n][i];
return cnt;
可見,這是乙個o(n^3*m)的演算法,僅適合規模小的情況
(2) 改進這個規劃演算法,減少第3維的狀態,i,j的狀態含義有改變,它們此時的含義就是:將整數i劃分為j份的方案數。根據類似於「堆積木」的思維,當前狀態實際上就是現在最下一行各擺上「1」塊積木接下來就是把「i-j」塊積木放上去並保持階梯狀,實際就是「i-j」拆分成「0~k(k <= j)」份的方案總數之和,所以有:f[i][j] = sum
for (i = 1; i <= n; i++)
for (j = 1; j <= (i > m ? m : i); j++)
for (k = 0; k <= j; k++)
f[i][j] += f[i-j][k]
return f[n][m];
可見,這是乙個o(n*m^2)的演算法,仍然不怎麼適用於大規模的n, m
(3) 是否可以繼續化簡呢?可以從方程入手,首先我們要有乙個概念,那就是:如果
dp中要用到對前面狀態求和,那麼要麼可以
o(n^2)
預處理sum[i][j]
,要麼就可以簡化
dp方程,使得
(i, j)
僅僅與(i-1, j)
或者(i, j-1)
有關。這裡我們從方程入手:f[i-1][j-1] = sum
= sum
觀察下標k的變化,方程可簡化為:f[i][j] = f[i-1][j-1] + f[i-j][j] --- (*)
for(i = 1; i <= n; i++)
for(j = 1; j <= (i > m ? m : i);j++)
f[i][j] = f[i-1][j-1] + f[i-j][j];
return f[n][m];
可見,時間複雜度降為min
小節:至此,該基本問題劃分演算法的動態規劃演算法設計完成,2次方的時間複雜度基本可以滿足較大規模的計算,不過計算的時候要注意,隨著n, m增大,f值增加的非常之快,所以大規模時,f選用double才是正確的。最後,如果想輸出所有可能的劃分方案,上面規劃演算法所記錄的資訊太少了。更為**的是,方案數隨著n, m成指數增長,可以說巨大無比,對於大規模n,m時輸出不要太多。
二.一些變形的劃分問題
1.劃分為不重複出現的元素的方案數。問題簡述:n個物體,價值分別為p[i],求組成價值為s的方案總數,這裡就是乙個不能重複使用的類似於揹包的問題,本質上也就是我前面那個帖子中「前10個階乘所能構成和為n的方法數」問題的擴充套件。具體以:
的1163題為例簡單的說一下:將數字n分成2份以上.使用的數字不可重複.例如5 = 1+4 = 2+3,就只有兩種拆分的方式。請輸出拆分的種數。
分析設計:首先,這裡n個物體非常特殊了,也就簡單不少,價值就是自己序號本身。然後,由於這裡所求劃分並非是單一限定了多少份,而是整個劃分數的方案總數,所以如果採用第一部分的分析來做,則至少需要o(n^3),而且還需要考慮不能重複,設計起來比較麻煩了。所以我們這裡需要另外挑選狀態了,類似於「階乘」中的處理方法,令f[i]表示組成和為i的方案總數,那麼f[i+j]就可以由所有的f[j]加上i而來,因此很簡單的得到遞推公式:
f[i+j] += f[j],0 <= j <= maxn – i;仍然需要注意的是,由於該遞推式是前面決定後面的,所以遞推時倒過來即可:
f[0] = f[1] = 1; // 組成0, 1的方法只有 1 種
for (i = 2; i <= maxn; i++) // 遞推
for (j = maxn – i; j >= 0; j--)
f[i+j] += f[j];
實際上,對於此類非重複組合問題,實際上就是裝揹包問題,只不過這裡不是限制揹包容量去求最優價值,而是倒過來使用某些物體去裝揹包,使得揹包的容量為特定值。這樣形式的問題,採用如上的方法再好不過了,實際上可能這種方法不應該叫規劃演算法了,應該叫做「生成函式」,即主動由乙個狀態去生成它所有可以生成的狀態(注意保證不重複)。不過我還是習慣叫他dp,覺得還是與狀態有關。
2.變形的整數劃分縮減版:
以的1738為例,這是乙個四完全平方數理論。
問題描述:四完全平方數理論理論:任何乙個正數都能表示為至多四個完全平方數之和。現希望你能寫出對於任乙個正數,它有多少種這樣的表示方式,eg:
25 = 1+4+4+16 = 9+16 = 25
共3種。也是不考慮順序關係,但是可以重複使用。
演算法設計:當時做這題時,沒有怎麼研究整數劃分演算法,所以一看到「至多4個完全平方數」,立馬就想到了「至多4重迴圈」列舉建表,再對應輸出即可。還作了些優化:例如建立好200以內的平方表,列舉i,j,k,l時剪掉超過範圍的,最後也可以在0.5 s內解決掉。但是實際上有效的解決這個問題的方法仍然是使用動態規劃演算法(也是主動生成法)。
(1) 首先看狀態:ans[i][k] 表示整數 i 可以表示成k個完全平方數形式的方案總數,那麼先對 i = 0,給出特殊的邊界條件:ans[0][1] = ans[0][2] = ans[0][3] = ans[0][4] = 1; 即0 總是可以表示成1個,2個,3個,4個 0^2 之和。
(2) 其次,進行遞推填充:
a. 先填充 k = 1:ans[i*i][1]++;即所有的 i*i為構成k = 1的情況
b. 對於k > 1:ans[j+i*i][k+1] += ans[j][k], 0 <= j < n - i*i; 1 <= k < 4;含義就是數j可以表示成k個完全平方數方案總數,那麼j再加上i*i。則表示數j+i*i的k+1個完全平方數方案總數的一部分 --- 這意義是很明顯的
預先計算好ans[i][k]陣列之後,那麼對於每一次的輸入n,輸出ans[n][k]即可。可見,這題仍然是由已有狀態去生成可以生成的狀態。因此可以簡單的小結為乙個規律:對於求劃分方案數的問題,可以首先考慮生成。其實前面的基本問題所推的演算法也是基於生成思想的.
3.求劃分因子乘積最大的乙個劃分及此乘積
問題簡述:給定乙個正整數n, 則在n所有的劃分中, 求因子乘積最大的乙個劃分及此乘積。例如:8 = , , , , , , 等,那麼在這些當中,3 * 3 * 2 的乘積最大,所以輸出整個劃分
和這個乘積 18。
演算法分析
(1)對於任意大於等於4的正整數m, 存在乙個劃分m = m1+m2, 使 m1*m2 >= m證: 令m1 = int(m/2), 則 m1 >= 2
m2 = m-m1; 那麼m2 > 2,並且 m2 >= m/2 >= m1;
m1*m2 >= 2*m2 >= m; 證畢;
該證明簡單的來說就是:對於乙個大於等於4的正整數m,存在乙個2塊劃分的因子,這兩個因子的乘積總是不小於原數m本身。
(2)由(1)知此數最終可以分解為 2^r * 3^s。現證明 r <= 2;
證:若r > 2, 則至少有3個因子為2, 而2*2*2 < 3*3;
所以可以將3個為2的因子,換為兩個因子3;積更大;證畢。
綜合(1),(2),則有:任何大於4的因子都可以有更好的分解, 而4可以分解為2*2。
所以:此數應該分解為 2^k1 * 3^k2。而且可以證明 k1>=0 並且 k1 <= 2,因此:
a.當n = 3*r 時, 分解為 3^r
b.當n = 3*r+1時, 分解為 3^(r-1)*2*2
c.當n = 3*r+2時, 分解為 3^r*2
剩下程式設計處理,那就是太簡單了,首先是處理
<= 4的特殊情況,再對》4的情況進行模3的3種情況的判斷,最後一一輸出。可見,數學在整數劃分問題上有太強的功能。誰叫這個問題叫整數劃分呢,不與數學密切才怪! ^_^。
整數劃分問題 演算法
在遞迴裡,要對形參進行判斷 1 當n 1時 q 1,m 表示是對1的劃分,那麼只有一種劃分方式 1 2 當m 1時q n,1 當m 1時其實就是把讓所有加數小於等於1,那就是所有加數都是1咯 不考慮負數 當然也只有一種劃分方式 3 當n m時q n,n 此時就是對n的劃分出來的數沒有限制,預設限制就...
整數劃分問題(遞迴演算法)
問題描述 將正整數n表示成一系列正整數之和,求有多少中劃分方法。例如正整數6有以下劃分方法 最大加數為6時,有1種劃分 6 最大加數為5時,有1種劃分 5 1 最大加數為4時,有2種劃分 4 2,4 1 1 最大加數為3時,有3種劃分 3 3,3 2 1,3 1 1 1 最大加數為2時,有3種劃分 ...
c 遞迴演算法整數劃分問題
整數劃分問題是演算法中的乙個經典命題之一,有關這個問題的講述在講解到遞迴時基本都將涉及。所謂整數劃分,是指把乙個正整數n寫成如下形式 n m1 m2 mi 其中mi為正整數,並且1 mi n 則為n的乙個劃分。如果中的最大值不超過m,即max m1,m2,mi m,則稱它屬於n的乙個m劃分。這裡我們...