对于这个问题, 我们显然可以看出来, 当他是奇数的时候, 直接等于他的前一个偶数
dp [ i ] = dp [ i - 1] ;
那么问题, 当它是偶数的时候, 我们应该怎么进行 dp 记忆化搜索并且递归?
不知你是否记得化分数问题, 不记得话,请看dp初级内容, 就在DP 内容
我们这里也是同样采取分成组内部有 1, 和分成组的内部没有 1 ;
当有一的时候, 那么就和上面的奇数一样, 具体说一下为什么, 以为它是偶数,一旦他有一, 那么至少为 2 个, 我们把这两个 1 进行合并, 然后看成 i - 1 中剩下的 一个 1 ,完成了递归
当没有一的时候, 我们可以直接 除以二进行处理, 以为最小化单元就可以看做之前的最小化单元 1 ,
所以说 dp [ i ] = dp [ i - 1] + dp [ i / 2 ] ;
下面是代码
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <cctype> #include <algorithm> #include <string> #include <vector> #include <queue> #include <list> #include <map> #include <stack> #include <set> using namespace std; const int MAX_N = 1000010; const int MOD = 1000000000; int dp[MAX_N]; // TM 递归会栈溢出, 这也太狗了吧!! int rec(int n){ if(dp[n] != -1) return dp[n]; else{ if(n & 1) //奇数 dp[n] = rec(n - 1); else{ dp[n] = (rec(n - 1) + rec(n >> 1)) % MOD; } } return dp[n]; } int main() { int n; memset(dp, 0, sizeof(dp)); dp[1] = 1; dp[2] = dp[3] = 2; cin>>n; for(int i = 4; i <= n; i++){ dp[i] = dp[i - 1]; if(!(i & 1)){ dp[i] += dp[i>>1]; } dp[i] %= MOD; } printf("%d ", dp[n]); return 0; }
除了这一种写法还会有另外一种的写法:
这个方法类似于背包
然后就是原来的基础上进行加上了新的 2 的倍数!
#include<iostream> #include<cstdio> using namespace std; const int mod = 1e9; const int maxn = 1000005; int main(){ int c[22] = {1}; long long int dp[maxn] = {0}; for(int i = 1; i < 22; i++) c[i] = c[i-1] * 2; dp[0] = 1; int n; cin >> n; for(int i = 0; i < 22; i++){ for(int j = c[i];j < n+1; j++){ //当他循环的时候, 相当于 在最大 为 c[i] 因子的限制条件下, 他的种类数目 dp[j] = dp[j] + dp[j-c[i]]; // 这个递归关系是相当于有了一个甚至多个 c[i] 的时候, 进行递归, 然后加上之前没有的 if(dp[j] > mod) dp[j] = dp[j] % mod; } } printf("%lld ", dp[n]); return 0; }
但是图片中有点小错误, 不知道你发现了没有, 应该是: dp [ i ] [ j ] = dp [ i - 1 ] [ j ] + dp [ i ] [ j - w [ i ] ] !!!
所以说, 上代码 :
#include <iostream> #include <cstdio> #include <algorithm> #define Maxn 1000005 using namespace std; int n; int w[Maxn]; int cnt=0; int dp[Maxn]; int main() { scanf("%d",&n); for(int i=0;(1<<i)<=n;i++)//构造所有物品 w[cnt++]=(1<<i); dp[0]=1; for(int i=0;i<cnt;i++) for(int j=w[i];j<=n;j++) dp[j]=(dp[j]+dp[j-w[i]])%1000000000;//取余 printf("%d ",dp[n]); return 0; }