poj 2229 Sumsets(记录结果再利用的DP)

传送门

https://www.cnblogs.com/violet-acmer/p/9852294.html

题意:

  将一个数N分解为2的幂之和共有几种分法?

题解:

  定义dp[ i ]为数 i 的分解方案数。

  初始化dp[0] = 2 ^ 0 = 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[2* 4];

  8分解成2的幂之和,只能分解成2, 2, 2, 23之间的加和。

  如果分解方案中含有20,并且不能出现重复,那可以考虑7的分解方案中的每个方案都+1 <=> 8的含20的分解方案总数(对应表中橘色部分);

  因为dp[7]中的分解方案数是不重复的,所以每个方案数+1也是不重复的;

  那,如何使分解方案中含有2, 2, 23呢?

  想一下4的分解方案数是怎么得到的?

  4分解成2的幂之,只能分解成2, 2, 22之间的加和,如果4中的每个方案都 *2,那不就正好变成8的分解方案中只含21 , 22 , 23的分解方案了吗(对应表中蓝色部分)?

  如果 i 为奇数,就不能通过某数 *2 来得到 i,那也就是说 (i-1) 方案中每个方案+1便可以得到 i 的所有分解方案,故dp[ i ]=dp[ i-1]

AC代码:

 1 #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 }
View Code

猜你喜欢

转载自www.cnblogs.com/violet-acmer/p/9931448.html