• DP:Sumsets(POJ 2229)


                     

                     数的集合问题

      题目大意:给定你一个整数m,你只能用2的k次幂来组合这个数,问你有多少种组合方式?

      这一题一看,天啦太简单了,完全背包?是不是?

      不过的确这一题可以用完全背包来想,但是交题绝对是TLE,如果真的是完全背包的做法那我就不用等那么多天再发这个坑,这一题的确要用到点奇妙的思想。

      首先,我们忽略了这一题的最重要的一个条件,我们使用的数就是2次幂的,那么2次幂的数可以做什么呢?这就是一个数学问题了

      不过不要怕,这个数学问题也很好想,

        首先:任何一个奇数一定有1来组成,推论:任何偶数都可以只由除了1的数组成

        其次,任何一个偶数都可以由一个数左移1来得到(参考二进制)

      下面我们就用这两个数学结论来思考怎么简化递推。

      回到问题上来,完全背包会TLE的原因是出在在第二个循环的时候对j进行了过多的枚举,那么我们在用这两个结论的时候必须避开这一点,最好一步到位,所以我们必须把个数全部压在前一次的情况上,那么我们可首先用结论1,对于任何一个奇数,我们都可以用上一个偶数+1(组合数不变,因为只能加这个1),且这个集合不能由其他集合直接得到,那么我们就得到第一个递推公式

       dp[j]=dp[j-1]  当j=奇数

      现在用到第二个结论,因为我们的偶数可以从奇数得到,也可以从偶数得到,那么可以第一部分可以从dp[j-1]得到,另外一个部分就要思考结论2,因为我们只是左移,组合数是不变的,所以我们还可以从dp[j>>1]中得到另一部分的组合数,这样就避开了一个一个查找枚举i的背包了

      所以综上,状态转移方程为:

        dp[i]=dp[i-1]  当i是奇数

        dp[i]=dp[i-1]+dp[i>>1]  当i是偶数  

        (注意这一题只显示9个数字)

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #define M 1000000000
     4 
     5 long long Combinatories[1000001];
     6 
     7 int main(void)//这一题不能用完全背包,会超时
     8 {
     9     int N, i;
    10     Combinatories[1] = 1;//这个地方要设置成1
    11 
    12     for (i = 2; i < 1000001; i++)
    13     {
    14         if (i % 2 == 1)
    15             Combinatories[i] = Combinatories[i - 1];
    16         else
    17             Combinatories[i] = Combinatories[i - 1] + Combinatories[i >> 1];
    18         Combinatories[i] %= M;
    19     }    
    20 
    21     while (~scanf("%d", &N))
    22         printf("%d
    ", Combinatories[N] % M);
    23 
    24     return 0;
    25 }

     

  • 相关阅读:
    Linux安装python3.6
    Django之Model操作
    Django
    html学习笔记-XML-Javascript
    html学习笔记-XML
    html学习笔记-DOM
    在IDEA中编辑struts国际化properties文件
    Java中的Serializable接口和transient关键字
    关于Kettle的事务和转换内步骤的顺序执行
    Mac、Linux下两个Emacs共享一个配置文件
  • 原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/4840906.html
Copyright © 2020-2023  润新知