简单记录在此
题意简单介绍:一个数字只用二进制数表达,方法有多少种,举例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:
完成