高深的dp POJ 2229Sumsets

    对于这个问题, 我们显然可以看出来, 当他是奇数的时候, 直接等于他的前一个偶数  

    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;
}
原文地址:https://www.cnblogs.com/lucky-light/p/11504786.html