zoukankan      html  css  js  c++  java
  • POJ 2229 sumset ( 完全背包 || 规律递推DP )

    题意 : 给出一个数 n ,问如果使用 2 的幂的和来组成这个数 n 有多少种不同的方案?

    分析 : 

    完全背包解法

    将问题抽象==>有重量分别为 2^0、2^1、2^2…2^k 的物品且每种物品可无限取,问有多少种方案来填满容量为 n 的背包?

    之前并不知道背包还能用来计数.......

    有一道裸的背包计数问题可以作为练习 ==> HDU 1284

    定义 dp[ i ][ j ] 为前 i 种物品组成总重量 j 的方案数为多少、初始化为 dp[ 0 ][ 0 ] = 1 其他为 0

    则状态转移方程为  dp[ i ][ j ] += dp[ i-1 ][ j - k*w[ i ] ] ( k ≥ 0 && j ≥ k*w[i] )

    最后类似于完全背包的递推方程,可化简为一维线性的递推式 dp[ j ] += dp[ j - w[ i ] ] ( j ≥ w[i] )

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e6 + 10;
    const int mod  = 1e9;
    int dp[maxn];
    int main(void)
    {
        int n;
        while(~scanf("%d", &n)){
            memset(dp, 0, sizeof(dp));
            dp[0] = 1;
            for(int i=0; i<20; i++)
                for(int j=(1<<i); j<=n; j++){
                    dp[j] += dp[j-(1<<i)];
                    if(dp[j] >= mod) dp[j] -= mod;
                }
            printf("%d
    ", dp[n]);
        }
        return 0;
    }
    View Code

    找递推规律解法

    现分别来考虑 n 为奇数还有偶数的情况

    ① n 为奇数的时候可以发现只是在 n-1( 偶数 ) 每种方案的后面多了个 1 而已并不能多组出新的方案,所以 dp[ 奇数 ] = dp[ 奇数 -1 ]

    ② n 为偶数,此时可以将所有的方案数分成两类 ( 组合方案中包含 1 的 ) 与 ( 组合方案中不包含 1 的 ) 

    首先来看组合方案中包含 1 的情况

    可以将其看成在 n-1 的方案中每个方案的后面多加一个 1 来组成,此时方案数和 n-1 是一样的即 dp[ n - 1 ]

    而组合方案中不包含 1 的情况

    如果将小数据打表列出来会发现这种情况的方案数实际等于 n/2 的方案数,即 dp[ n/2 ]

    所以最后的答案应该为 dp[ n ] = dp[ n-1 ] + dp[ n/2 ]

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e6 + 10;
    const int mod  = 1e9;
    int dp[maxn];
    int main(void)
    {
        int n;
        while(~scanf("%d", &n)){
            dp[1] = 1;
            dp[2] = dp[3] = 2;
            dp[4] = dp[5] = 4;
            dp[6] = dp[7] = 6;
            if(n <= 7) printf("%d
    ", dp[n]);
            else{
                for(int i=8; i<=n; i++){
                    if(i&1) dp[i] = dp[i-1];
                    else dp[i] = dp[i-2] + dp[i>>1];
                    if(dp[i] >= mod) dp[i] -= mod;
                }
                printf("%d
    ", dp[n]);
            }
        }
        return 0;
    }
    View Code

    现举几个例子来解释一下

    dp[1] = 1

    1

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

    dp[2] = 2

    1+1、2

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

    dp[3] = dp[3-1] = 2 

    1+1+1、2+1 ( 奇数情况 == 奇数-1中所有方案数后面添 1 )

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

    dp[4] = dp[4-1] + dp[4/2] = 4

    1+1+1+1、2+1+1 ( 这个就是 dp[4-1] 的情况 == 在 n-1 的所有方案数后面添 1 )

    2+2、4 ( 这里的所有方案 / 2 后会发现实际就对应了 dp[2] ,所以是 dp[ 4/2 ] )

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

    dp[5] = dp[5-1] = 4

    1+1+1+1+1、2+1+1+1

    2+2+1、4+1

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

    dp[6] = dp[6-1] + dp[ 6/2 ] = 6

    1+1+1+1+1+1、2+1+1+1+1、2+2+1+1、4+1+1 ( 此为 dp[ 6-1 ] 意义和上面所述一样 )

    2+2+2、4+2 ( 方案所有数 / 2 后变成 1+1+1、2+1 和 dp[3] 是对应的! )

    ......

  • 相关阅读:
    多项式 Wannafly挑战赛22
    L. Twice Equation ACM Nanning 2017
    我们身边的大数据
    js_隔10秒发送验证码(setInterrval定时器)
    js_定时器(setInterval)
    VS调试相关
    afx_msg与消息映射机制
    ON_COMMAND,ON_MESSAGE和ON_NOTIFY的区别
    条款4:确定对象在被使用前已经被初始化
    用doxygen+graphviz生成函数调用流程图
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/8118228.html
Copyright © 2011-2022 走看看