整數劃分系列問題(動態規劃)

2021-07-05 06:54:03 字數 3199 閱讀 2693

今天上演算法分析與設計課時,提到整數劃分問題,但是因為之前沒有很好地理解這一系列的問題。當被老師問到你們做演算法的應該會這個問題吧,我當時也不太記得,但是只能硬頭皮試著去寫了下,用的是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劃分成若...