soj2785_背包装满

简单记录在此

题意简单介绍:一个数字只用二进制数表达,方法有多少种,举例3=1+1+1=1+2。两种

看到某一个博主介绍自己的思想是,通过递推,二进制角度,任何数n都等于(n-1)+1,即用n的前一个数加1表示。

如果该数n是偶数,那么他的组成方式就是 { n-1的组成方式种数+他的一半n/2的组成种数 }。也就是dp(n)=dp(n-1)+dp(n/2);

n/2左移一位即扩大为n,所以n/2的所有组成方式都*2则可以成为n的组成方式,再加上dp(n-1)就全面了。

举例两个数 6,3,顺便写一下5


5                                   6                                   3

1 1 1 1 1                 1 1 1 1 1 1

1 1 1 2                    11 1 1 2 

1 2 2                       1 1 2 2 

                               2 2 2                                1 1 1

                               2 4                                   1 2


如上表示,红色的代表n-1的每一种组成都加一个1,绿色表示n/2的每种表示乘2,加起来就构成了现在的n的组成方式。

所以程序主要代码如下

    f[0] = 1;  
    for(int i = 1;i < 2000010; i++)  //0到2,000,000之间的所有种数。
    {  
        if(i%2 == 0)  
            f[i] = (f[i/2] + f[i-1]) % mod;  
        else
            f[i] = f[i-1] % mod;
    } 

------------------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------------------

但是这样就是比较偷懒的简单方法,实际上这道题是一个装满的背包。

可以看做装满2^0,2^1,2^2,2^3…2^n<max的背包,只需要一个一个取出来就行。

所以代码如下


    dp[0] = 1;
    for (int i = 1; i <= max; i <<= 1) 
        for (int j = i; j <= max; j++) 
            dp[j] += dp[j - i] ;
这段代码是将所有数值组成种数都先算出来存起来,要哪个直接用.

最初不明白,因为按照这段代码,dp[5]=dp[4]+d[3]+dp[1]=4+2+1=7,但是dp[5]=4,我认为有问题,所以不是很明白,那我们就从头到尾看一下过程:

假设我们的五个测试数为1 2 3 4 5,设最大值为max(防止计算不必要的特别大的值),此处max = 5


可以看出i的取值,除了i = 1时,其他情况是所求值包含 i 的种数,这个在前一部分已经求出。

dp[j-i] 就是 j 的各种组合中包含 i 的种数,并且已知,通过运用前面求出的小的数值。

每次增加一个二进制数,可以由小的数到大的数来加,并且小的数的结果可以直接被大的数利用。

对于我的疑问,通过过程,确实dp[5] = dp[4]+dp[3]+dp[1],但是每一步都用到了前面得到的值,相当于一个一个提取,有多少种,不是直接将结果相加。

所以规律就是 dp[ j ] += dp[ j - dp[2^n] ] + dp[0]  (2^n < j, n > 0 ) 就行了,所以dp[5] = dp[3] + dp[1] + dp[0]

如果还不明白,按照第一种方法,我们把dp[5]拆开

方法1:首先dp[0] = dp[1]


方法2:


完成

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值