今天上演算法分析與設計課時,提到整數劃分問題,但是因為之前沒有很好地理解這一系列的問題。當被老師問到你們做演算法的應該會這個問題吧,我當時也不太記得,但是只能硬頭皮試著去寫了下,用的是brute force,老師果斷說我的方法跑不出正確的解,但是課堂上我也沒有去解釋了。課後自己驗證了一下,沒有問題,但是看了一下更好的動態規劃的解法,順便把幾種整數劃分的型別都理解清楚。
原題:乙個整數劃分為多個數的和,有幾種劃分的組合。
// 對num劃分,最大的數不超過k
int divide(int num, int k)
int res = 0;
for (int i = k; i > 0; i --)
}return res;
}
這個寫法就是列舉了,效率肯定十分慢,不加記憶化的遞迴也快不到哪去。
加了記憶化後,n稍微大點還是跑不出來。
int d[1000][1000];
// 對num劃分,最大的數不超過k
int divide(int num, int k)
if(d[num][k] > 0)
int res = 0;
for (int i = k; i > 0; i --)
}return res;
}
但是學會整數的劃分動歸求解,就可以在o(n*n)的時間複雜度內求解!!
下面進入正題。
整數劃分問題變型比較多,但是有句話叫做萬變不離其宗,這是看了兩個貼吧大神(飛機)的講解後領悟的,所以索性5個整數劃分的問題全部列出來,自己也整理一下。
一: 將n劃分成若干正整數之和的劃分數。
二: 將n劃分成k個正整數之和的劃分數。
三: 將n劃分成最大數不超過k的劃分數。
四: 將n劃分成若干奇正整數之和的劃分數。
五: 將n劃分成若干不同整數之和的劃分數。
引用一下9樓的解釋,結合7樓的**。
先做乙個定義:
定義:dp(i, j)表示整數i被劃分為j個數的劃分數。
那麼有dp(i, j) = dp(i - j, j) + dp(i - 1, j - 1)
上述劃分數可以分解為兩類情況:
1、分解的數含1, 即dp(i-1, j-1);
2、分解的數不含1, 那麼先將分成的j個數減1,然後得到i-j,劃分為j個數。即dp(i - j, j)。
問題二:將n劃分成k個正整數之和的劃分數 解決, 程式為:
void divide2(int n, int k)
}}
問題三:將n劃分成最大數不超過k的劃分數。
即可以轉化為:∑ dp(n, i) (1<=i<=k)
int divide3(int n, int k)
return res;
}
但是這麼做就需要先求出問題二的dp陣列了,有沒有其他方法呢?
答案是有的
但是需要換乙個思路:dp2[i][j]陣列的定義為i劃分最大數不超過j的劃分數。
那麼dp2[i][j] = dp2[i-j][j] + dp2[i][j-1]
dp2[i][j-1]表示劃分數所有數都小於j的情況數。
dp2[i-j][j]表示每種情況都含
至少乙個
最大數j, 先保留乙個j,剩下的i-j進行劃分,最大數仍為j。
比如dp2[9][4],先分乙個4,然後dp2[5][4]還可以再分乙個4出來,因此劃分出來值是可以重複的。
void divide3(int n)
if(i == j)
if(i > j) }}
}
問題一:將n劃分成若干正整數之和的劃分數。
只要讓問題3中的∑ dp(n, i) (1<=i<=n) 或者dp2[n][n]
問題五:將n劃分成若干不同整數之和的劃分數
上面我寫了這麼一句話:
「dp2[i-j][j]表示每種情況都含
至少乙個
最大數j, 先保留乙個j,剩下的i-j進行劃分,最大數仍為j。
比如dp2[9][4],先分乙個4,然後dp2[5][4]還可以再分乙個4出來,因此劃分出來值是可以重複的。」
所以要保證不重複,只需要
dp2[i-j][j-1]
即可。**這一處改了就不寫了。
問題四:將n劃分成若干奇正整數之和的劃分數。
dp3[i][j] = dp3[i-j][j] + dp[i][j-2],其中j為奇數,發現其實和問題1一樣的,只是在迴圈控制的時候,保證j+=2就可以了,然後討論一下幾個特殊情況,看最後的**吧。
如果類似由問題1變型為問題5也只需要將dp3[i-j][j]變為dp3[i-j][j-2]即可。
至此,五個問題均得到解決,參考貼吧7樓的**,發現還是沒有得到想要的答案還是按照自己的思路寫吧。另外如果覺得迴圈寫不好,記憶化搜尋也是一種選擇。
#include #include #include using namespace std;
const int maxn = 100;
int dp[maxn][maxn]; // 將i劃分為最大不超過j的整數
int dp2[maxn][maxn]; // 將i劃分為j個整數
int dp3[maxn][maxn];
int dp4[maxn][maxn];
int n, k;
// include or not include j
void solve13(int n)
if(i == j)
if(i > j) }}
}// include or not include 1
// 為什麼上面不寫迴圈條件 j<=i, 因為i-j < j時dp[i-j][j]不是0!!!
void solve2(int n)
}}void solve4(int n)
if(i == j)
if(i < j) }}
}void solve5(int n)
if(i == j)
if(i > j) }}
}void init()
int main()
return 0;
}
整數劃分問題 動態規劃
原文出處 整數劃分 有以下情況 1 將n劃分成若干正整數之和的劃分數。2 將n劃分成k個正整數之和的劃分數。3 將n劃分成最大數不超過k的劃分數。4 將n劃分成若干奇正整數之和的劃分數。5 將n劃分成若干不同整數之和的劃分數。include includeconst int ns 55 int n,...
整數劃分問題 動態規劃 遞迴
將乙個整數 n 劃分為 不超過m 組 的劃分數 如 n 4m 3 輸出 4 思路 使用動態規劃 定義狀態 dp i j j的i劃分的組數 遞推 dp i j dp i j i dp i 1 j 當m n時,變成了常見的整數劃分問題 cpp view plain copy include includ...
動態規劃 整數劃分問題(2)
整數劃分 乙個老生長談的問題 1 練練組合數學能力.2 練練遞迴思想 3 練練dp 總之是一道經典的不能再經典的題目 這道好題求 1.將n劃分成若干正整數之和的劃分數。2.將n劃分成k個正整數之和的劃分數。3.將n劃分成最大數不超過k的劃分數。4.將n劃分成若干奇正整數之和的劃分數。5.將n劃分成若...