1. 程式人生 > >POJ 2229 sumset ( 完全背包 || 規律遞推DP )

POJ 2229 sumset ( 完全背包 || 規律遞推DP )

如果 技術分享 view strong esp alt 裏的 情況 ons

題意 : 給出一個數 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;
        
for(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; }
View Code

找遞推規律解法

現分別來考慮 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 )