1. 程式人生 > >BZOJ 1677 [Usaco2005 Jan]Sumsets 求和:dp 無限背包 / 遞推【2的冪次方之和】

BZOJ 1677 [Usaco2005 Jan]Sumsets 求和:dp 無限背包 / 遞推【2的冪次方之和】

zoj mem iostream memset bzoj -1 target ont 背包

題目鏈接:http://www.lydsy.com/JudgeOnline/problem.php?id=1677

題意:

  給定n(n <= 10^6),將n分解為2的冪次方之和,問你有多少種方法。

題解:

  兩種方法。

  一、無限背包

    將1,2,4,8...看作物品體積就好。

    復雜度O(n*k),k約為20。

  二、遞推

    對於dp[i],有兩種情況。

      (1)i為奇數。則分解結果中一定有1。

          所以dp[i] = dp[i-1]。

      (2)i為偶數。再分兩種情況:

          a. 分解結果中有1,所以dp[i] += dp[i-1]

          b. 分解結果中沒有1,即所有加數都是2的倍數。可以將所有加數都除以2,所以dp[i] += dp[i/2]

          綜上:dp[i] = dp[i-1] + dp[i/2]

AC Code(背包):

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define MAX_N 1000005
 5 #define MOD 1000000000
 6 
 7 using namespace std;
 8 
 9 int n;
10 int dp[MAX_N];
11 12 int main() 13 { 14 cin>>n; 15 memset(dp,0,sizeof(dp)); 16 dp[0]=1; 17 for(int i=0;i<=20;i++) 18 { 19 for(int j=(1<<i);j<=n;j++) 20 { 21 dp[j]=(dp[j]+dp[j-(1<<i)])%MOD; 22 } 23 } 24 cout<<dp[n]<<endl;
25 }

AC Code(遞推):

 1 // if is odd dp[i] = dp[i-1]
 2 // if is even dp[i] = dp[i-1] + dp[i/2]
 3 #include <iostream>
 4 #include <stdio.h>
 5 #include <string.h>
 6 #define MAX_N 1000005
 7 #define MOD 1000000000
 8 
 9 using namespace std;
10 
11 int n;
12 int dp[MAX_N];
13 
14 int main()
15 {
16     cin>>n;
17     memset(dp,0,sizeof(dp));
18     dp[0]=1;
19     for(int i=1;i<=n;i++)
20     {
21         if(i&1) dp[i]=dp[i-1];
22         else dp[i]=(dp[i-1]+dp[i>>1])%MOD;
23     }
24     cout<<dp[n]<<endl;
25 }

BZOJ 1677 [Usaco2005 Jan]Sumsets 求和:dp 無限背包 / 遞推【2的冪次方之和】