整數劃分總結

2021-07-09 22:36:16 字數 3604 閱讀 8714

整數劃分問題:

籠統上說就是將一根整數劃分成若干個整數之和的方案數。整數劃分很多不同的問法,也有隱晦的問法。比如n個蘋果放到m個盤子裡,比如n個磚塊堆成m個層階梯。關於整數劃分,大概有以下這麼多擴充套件的問題:

1,整數n劃分成若干整數之和的方案數;

2,整數n劃分成k個整數之和的方案數;

3,整數n劃分成最大數不超過k的若干整數之和的方案數;

4,整數n劃分成最小數不低於k的若干整數之和的方案數;

5,整數n劃分成若干不同的整數之和的方案數;

6,整數n劃分成k個不同整數之和的方案數;

7,整數n劃分成最大數不超過k的若干不同整數之和的方案數;

8,整數n劃分成最小數不低於k的若干不同整數之和的方案數;

9,整數n劃分成若干奇數的方案數;

10,整數n劃分成若干偶數的方案數;

這麼多問題乙個乙個解決,其實解決其中乙個問題,相關聯的也迎刃而解。首先看第二個問題,為什麼不看第乙個問題呢?很簡單仔細思考一下,列舉所有的k不就是第乙個問題的解嘛。第二個問題的狀態轉移方程式 引入dp2表示整數i分成j個整數的方案數

memset(dp2,0,sizeof(dp2));

dp2[0][0]=1;

for(int i=1;i<=n;i++)

}

怎麼去解釋這個狀態轉移方程呢?你可以把整數i分成j個整數之和看成i個蘋果放到j個盤子裡,不允許有空盤子。j個盤子,可以分成兩種狀態,所有盤子裡最小的蘋果數等於1,和大於1。等於1的情況,把這個最小蘋果數的盤子拿掉,dp[i][j]=dp[i-1][j-1];若最小數大於1,那麼把所有j的盤子都拿去乙個蘋果,dp[i][j]=dp[i-j][j]。那麼第乙個問題就也解決了。

現在看第三個問題,最大數不超過k的劃分數

memset(dp3,0,sizeof(dp3));

dp3[0][0]=1;

for(int i=0;i<=n;i++)

}

解釋這個狀態轉移方程:dp[i][j]表示i分成最大數不超過j的劃分數。dp[i][j]可以分成以下兩類:

1.最大數等於j;

2.最大數小於j;

對於第二種情況,滿足劃分條件的是dp[i][j-1],表示數i分成最大數不超過j-1的劃分數,那就是數i分成最大數小於j的劃分數。第一種情況,滿足劃分條件的是dp[i-j][j];dp[i-j][j]表示數i-j分成最大數不超過j的劃分數,然後將『+j』附加在每乙個劃分後面,就得到數分成最大數等於j的劃分數。那麼dp[i-j][j]的劃分數就等於dp[i][j]的劃分。

那麼看一到例題吧

poj 1664 放蘋果

其實,放蘋果問題,因為可以允許有空盤子,乙個盤子裡的蘋果最多不超過k個盤子數,所以這個問題就可以轉換成數i分成最大數不超過k的劃分數

貼一下ac**吧

#include 

#include

#include

#include

#include

#include

using

namespace

std;

int dp[15][15];

int n,m;

void fun()

}}int main()

}

第三個問題解決之後,也可以解決第乙個問題了,就是把最大數不超過k的k從1到n列舉一下就可以了。

第四個問題和第三個問題很相似,

先看狀態轉移方程

memset(dp4,0,sizeof(dp4));

for(int i=0;i<=n;i++)

dp4[0][i]=1;

for(int i=1;i<=n;i++)

}

同樣的,dp[i][j]可以分成兩大類

1.最小數等於j;

2.最小數大於j;

滿足第二個情況的劃分條件是dp[i][j+1],表示數i分成最小數大於j的情況。滿足第乙個情況的劃分條件是dp[i-j][j],表示i-j的最小數不小於j的劃分,然後將『+j』附加在每乙個劃分後面,就得到數i分成最小數等於j的劃分。同理列舉k可以得出第乙個問題的解

第五個問題,到這裡可以看出前四個是乙個型別,後面四個是另乙個型別,後面多出乙個要求,就是要求不同的數字。同理第五個問題是由第六個,七個,八個問題求解出來。

那麼直接看第六個問題,這裡我們和前面的聯絡起來看,因為只多了乙個數字不同的條件

先看狀態轉移方程

memset(dp6,0,sizeof(dp6));

dp6[0][0]=1;

for(int i=1;i<=n;i++)

}

可以看出和第二個問題狀態轉移方程的區別就是一點,dp[i][j-1]變成dp[i-j][j-1]。這裡我們不用放蘋果,我們用i個磚塊堆成有j個階次的階梯,階梯要求不能有相鄰兩個是同樣的高度

狀體轉移方程可以描述為,把階梯最底層的一層磚塊全部拿掉,階梯要麼還是不變的階數,要麼階數少了一階。

所以dp[i][j]=dp[i-j][j]+dp[i-j][j-1];

第五個問題直接列舉j就好了。

這裡有一道例題,就是明白著的問題六

hoj 1090

第七個問題,直接看狀態轉移方程

memset(dp7,0,sizeof(dp7));

dp7[0][0]=1;

for(int i=0;i<=n;i++)

}

和問題四的區別就是dp[i-j][j]變成了dp[i-j][j-1]

同樣的問題分成兩類,第二類是不變的dp[i][j-1],第一類變成了dp[i-j][j-1],對於第一類,dp[i-j][j-1]表數i-j分成最大數不超過j-1的劃分數,也就是i-j分成最大數小於j,將『+j』附加在每個劃分後面,得到數i分成最大數等於j的劃分。這樣就避免了有重複的,因為dp[i-j][j]表示最大數不超過j,就有等於j的情況,那麼再加上j就有重複的情況了。

第八個問題,就應該很好理解了

附**

memset(dp8,0,sizeof(dp8));

for(int i=0;i<=n+1;i++)

dp8[0][i]=1;

for(int i=1;i<=n;i++)

}

第九問題,分成若干個奇數

只需要判斷一下奇偶性就好了

**

memset(dp9,0,sizeof(dp9));

dp9[0][0]=1;

for(int i=0;i<=n;i++)

else

dp9[i][j]=dp9[i][j-1];}}

最後乙個,就不用說了。

寫到這裡,就應該差不多了。感謝老王對我說的一句話:

「我覺得潛心研究乙個東西就能有顯著提高」

所以,我想說,搞acm,刷題數量並不是最重要的,不要去關注total accept。去反覆研究乙個問題,它帶給你的,有時候比刷二十道,三十道,還要多,還要有用。

整數劃分總結

整數劃分問題 籠統上說就是將一根整數劃分成若干個整數之和的方案數。整數劃分很多不同的問法,也有隱晦的問法。比如n個蘋果放到m個盤子裡,比如n個磚塊堆成m個層階梯。關於整數劃分,大概有以下這麼多擴充套件的問題 1,整數n劃分成若干整數之和的方案數 2,整數n劃分成k個整數之和的方案數 3,整數n劃分成...

整數劃分(劃分dp)總結

寫了幾個題發現整數劃分是一類題,而不是一道題。具體題型 1 n相同元素放入m個相同的盤子 盤子允許為空 例題 放蘋果 poj 1664設dp i j 為 j 個元素放入i個盤子轉移方程 dp i j dp i 1 j 新新增乙個盤子,盤子為空 dp i j dp i j i i個盤子 各取出乙個 2...

整數劃分問題

整數劃分問題是乙個經典問題,幾乎在講演算法設計的書中都會講,下面把主要的思想給總結下。所謂整數劃分,就是將乙個正整數n劃分為一系列的正整數之和,如將n可以劃分為 1 我們該如何找出所有的劃分呢?我們可以先來看看整數劃分的規律 譬如正整數 6 劃分情況如下 6 5 14 2 4 1 1 3 3 3 2...