POJ 2229 sumset ( 完全背包 || 規律遞推DP )
題意 : 給出一個數 n ,問如果使用不同 2 的冪的和來組成這個數 n 有多少種不同的方案?
分析 :
完全背包解法
將問題抽象==>有重量分別為 2^0、2^1、2^2…2^k 的物品且每種物品可無限取,問有多少種方案來填滿容量為 n 的背包?
之前並不知道背包還能用來計數.......
有一道裸的背包計數問題可以作為練習 ==> HDU 1284
定義 dp[ i ][ j ] 為前 i 種物品組成總重量 j 的方案數為多少、初始化為 dp[ 0 ][ 0 ] = 1 其他為 0
則狀態轉移方程為 dp[ i ][ j ] += dp[ i-1 ][ j - k*w[ i ] ] ( k ≥ 0 && j ≥ k*w[i] )
最後類似於完全背包的遞推方程,可化簡為一維線性的遞推式 dp[ j ] += dp[ j - w[ i ] ] ( j ≥ w[i] )
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int mod = 1e9; int dp[maxn]; int main(void) { int n; while(~scanf("%d", &n)){ memset(dp, 0, sizeof(dp)); dp[0] = 1;View Codefor(int i=0; i<20; i++) for(int j=(1<<i); j<=n; j++){ dp[j] += dp[j-(1<<i)]; if(dp[j] >= mod) dp[j] -= mod; } printf("%d\n", dp[n]); } return 0; }
找遞推規律解法
現分別來考慮 n 為奇數還有偶數的情況
① n 為奇數的時候可以發現只是在 n-1( 偶數 ) 每種方案的後面多了個 1 而已並不能多組出新的方案,所以 dp[ 奇數 ] = dp[ 奇數 -1 ]
② n 為偶數,此時可以將所有的方案數分成兩類 ( 組合方案中包含 1 的 ) 與 ( 組合方案中不包含 1 的 )
首先來看組合方案中包含 1 的情況
可以將其看成在 n-1 的方案中每個方案的後面多加一個 1 來組成,此時方案數和 n-1 是一樣的即 dp[ n - 1 ]
而組合方案中不包含 1 的情況
如果將小數據打表列出來會發現這種情況的方案數實際等於 n/2 的方案數,即 dp[ n/2 ]
所以最後的答案應該為 dp[ n ] = dp[ n-1 ] + dp[ n/2 ]
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int mod = 1e9; int dp[maxn]; int main(void) { int n; while(~scanf("%d", &n)){ dp[1] = 1; dp[2] = dp[3] = 2; dp[4] = dp[5] = 4; dp[6] = dp[7] = 6; if(n <= 7) printf("%d\n", dp[n]); else{ for(int i=8; i<=n; i++){ if(i&1) dp[i] = dp[i-1]; else dp[i] = dp[i-2] + dp[i>>1]; if(dp[i] >= mod) dp[i] -= mod; } printf("%d\n", dp[n]); } } return 0; }View Code
現舉幾個例子來解釋一下
dp[1] = 1
1
------------------------------------------------------------------------------------
dp[2] = 2
1+1、2
------------------------------------------------------------------------------------
dp[3] = dp[3-1] = 2
1+1+1、2+1 ( 奇數情況 == 奇數-1中所有方案數後面添 1 )
------------------------------------------------------------------------------------
dp[4] = dp[4-1] + dp[4/2] = 4
1+1+1+1、2+1+1 ( 這個就是 dp[4-1] 的情況 == 在 n-1 的所有方案數後面添 1 )
2+2、4 ( 這裏的所有方案 / 2 後會發現實際就對應了 dp[2] ,所以是 dp[ 4/2 ] )
------------------------------------------------------------------------------------
dp[5] = dp[5-1] = 4
1+1+1+1+1、2+1+1+1
2+2+1、4+1
------------------------------------------------------------------------------------
dp[6] = dp[6-1] + dp[ 6/2 ] = 6
1+1+1+1+1+1、2+1+1+1+1、2+2+1+1、4+1+1 ( 此為 dp[ 6-1 ] 意義和上面所述一樣 )
2+2+2、4+2 ( 方案所有數 / 2 後變成 1+1+1、2+1 和 dp[3] 是對應的! )
......
POJ 2229 sumset ( 完全背包 || 規律遞推DP )