BZOJ 1677 [Usaco2005 Jan]Sumsets 求和:dp 无限背包 / 递推【2的幂次方之和】

题目链接: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 }

 

posted @ 2017-10-04 14:52  Leohh  阅读(366)  评论(0编辑  收藏  举报