• 背包方案数问题(礼物)


    给个题面——hdu2126

    数字组合-背包方案数问题

    简述:给你n块钱,有m种钱币,每种钱币只能用一次,问组成n块钱有多少种方法。

    给个数据(结果对1e7+7取模):

    Input

    6 25

    8 9 8 7 16 5

    Output

    15

    Input

    30 250

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

    output

    6509431

    Analysis

    这是一道想法很清奇的dp题。思维难度和代码难度都较大。

    既然你要考虑用dp做,那你肯定会毫无疑问地去选择前(i)个物品,选(j)个和钱数(k)作为状态,用背包来计算方案数。

    也就是

    [dp[i][j][k] += dp[i-1][j][k] ]

    [if(k >= v[i]) dp[i][j][k] += dp[i-1][j-1][k-v[i]] ]

    事实上这样会超时。

    所以要怎么做?

    有大佬用更好的方法,思路诡异。

    先将物品排升序,枚举状态(i),表示第(i)个物品是未被选的物品中价值最小的一个物品。

    那么价值比它小的物品,都在它左面,而且都会被选,把他们用前缀和维护起来。

    现在你剩余一些钱(j),从后面选取一些物品使价值最大但不超过(j),这就是背包。

    所以一共二维,时间复杂度空间复杂度皆降低,完美。

    咳咳,这是人能想出来的吗……

    Code

    #include<iostream>
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    const int MAXN=1010;
    const int mod=1000000007;
    using namespace std;
    ll dp[MAXN][MAXN],n,m,minn,w[MAXN];
    ll sum[MAXN];
    int cmp(int a,int b){ return a>b; }
    
    int main(){
        scanf("%lld%lld",&n,&m);
        for ( int i=1;i<=n;++i) scanf("%lld",&w[i]); dp[0][0]=1;
      sort(w+1,w+n+1,cmp);
        for (int i=n;i;--i) sum[i]=sum[i+1]+w[i];
        ll ans=0;
        for(int i=1;i<=n;i++){
            for(int j=m-sum[i+1];j>=0&&j>m-sum[i+1]-w[i];j--)
                ans+=dp[i-1][j],ans%=mod;
            for(int j=m;j>=0;j--){
                dp[i][j]=dp[i-1][j];
                if(j-w[i]>=0) dp[i][j]+=dp[i-1][j-w[i]],dp[i][j]%=mod;
            }
        }
        if(sum[1]<=m) ans++,ans%=mod;
        printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    NOIP2018 模拟赛(二十二)雅礼NOI
    浅谈左偏树在OI中的应用
    HDU3062&&HDU1814
    2-SAT超入门讲解
    bitset常用用法&&简单题分析
    NOIp2014提高组初赛错题简析
    2018十月刷题列表
    BZOJ 4804: 欧拉心算
    Luogu P2568 GCD
    Luogu P4137 Rmq Problem / mex
  • 原文地址:https://www.cnblogs.com/Ch-someone/p/9607682.html
Copyright © 2020-2023  润新知