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 }