poj 2229 Sumsets(类似于n的m划分)
•题意
将一个数N分解为2的幂之和共有几种分法?
•题解
定义dp[ i ]为 i 的分解方案数。
初始化dp[0] = 20 = 1;
状态转移方程为:
for i : 1 to N
若 i 为偶数,则dp[ i ] = dp[ i / 2] + dp[i – 1] ;
否则dp[i] = dp[ i – 1];
对状态转移方程的理解:
打个表先~~~~
i i 的分解方案
1......1
2......1+1,2
3......1+1+1,2+1
4......(2+1+1),(1+1+1+1),(2+2),(4)
5......2+1+1+1,1+1+1+1+1,2+2+1,4+1
6......2+1+1+1+1,1+1+1+1+1+1,2+2+1+1,4+1+1,2+2+2,4+2
7......(2+1+1+1+1+1),(1+1+1+1+1+1+1),(2+2+1+1+1),(4+1+1+1),(2+2+2),(4+2+1)
8......(2+1+1+1+1+1+1),(1+1+1+1+1+1+1+1),(2+2+1+1+1+1),(4+1+1+1+1),(2+2+2),(4+2+1+1),(4+2+2),(2+2+2+2),(4+4),(8)
以8的为例,dp[8]=dp[20+7]+dp[21 * 4];
8分解成2的幂之和,只能分解成20 , 21 , 22 , 23之间的加和。
如果分解方案中含有20,并且不能出现重复,那可以考虑7的分解方案中的每个方案都+1 <=> 8的含20的分解方案总数(对应表中橘色部分);
因为dp[7]中的分解方案数是不重复的,所以每个方案数+1也是不重复的;
那,如何使分解方案中不含有20呢?
想一下4的分解方案数是怎么得到的?
4分解成2的幂之,只能分解成20 , 21 , 22之间的加和;
如果4中的每个方案都 ×2,那不就正好变成8的分解方案中只不含有20的分解方案了吗(对应表中蓝色部分)?
如果 i 为奇数,就不能通过某数 ×2 来得到 i;
那也就是说只能通过 (i-1) 方案中每个方案+1 得到 i 的所有分解方案,故dp[ i ]=dp[ i-1]
•Code
View Code1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int MOD=1e9; 5 const int maxn=1e6+10; 6 7 int N; 8 int dp[maxn]; 9 10 int main() 11 { 12 scanf("%d",&N); 13 dp[0] = 1; // 2^0 14 for(int i=1;i <= N;++i) 15 { 16 if ((i & 0x1) == 0)//判断i是否为偶数 17 dp[i]=dp[ i / 2]; //将i/2的每个构成数乘以2,得到 i 18 dp[i] += dp[i - 1]; //将i-1的构成数拿过来加一 19 dp[i] %= MOD; 20 } 21 printf("%d\n",dp[N]); 22 return 0; 23 }
分割线:2019.6.16
•类比“n的m划分”
重新理解了一下“n的m划分”这种题的求解方法,想到了这道题;
感觉这道题和n的m划分很像;
n的m划分在状态转移时考虑的是“划分数种是否包含0这个元素”;
而在此题中,考虑的是“是否包含20这个元素”;
这应该是有两者的性质决定的,前者需要的是任意数的累加,后者需要的是2的幂的累加;
而任意数中的最小值为0,2的幂的最小值为20=1;
根据最小值的不同,考虑的不包含的数也不同;
此题中,数 i 的划分可分为两类:
①包含20;
②不包含20;
包含 20 很好办,直接将 i-1 的划分 +1 便可得到 i 的划分中包含 20 的划分方案数;
主要是不包含20要如何求解?
与n的m划分相仿,如果 i 为偶数,那么将 i/2 中划分 ×2 得到的就是 i 的划分不包含 20 的划分方案数;
根据上述讲解定义dp[ i ]表示 i 的划分方案数;
那么 dp[ i ]=dp[ i ]-1 + ( i为偶数 ? dp[ i/2 ] : 0);
dp[ 1 ] = 1;
•Code
View Code1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 #define ll long long 5 const int maxn=1e6+50; 6 const ll MOD=1e9; 7 8 int n; 9 ll dp[maxn]; 10 11 ll Solve() 12 { 13 dp[1]=1; 14 for(int i=2;i <= n;++i) 15 { 16 dp[i]=dp[i-1]; 17 if(!(i&1)) 18 dp[i] += dp[i>>1]; 19 dp[i] %= MOD; 20 } 21 return dp[n]%MOD; 22 } 23 int main() 24 { 25 scanf("%d",&n); 26 printf("%lld\n",Solve()); 27 return 0; 28 }