• Coins


    Coins

    有n个硬币,第i个硬币的价值(a_i),数量(c_i),现在求这些硬币组成的1~m以内的钱的个数,(1<=n<=100,m<=100000,c_ileq 1000)

    法一:二进制拆分优化01

    注意到这类似多重背包,多个选择,关键在于数据范围过大,于是可以采取二进制拆分优化。

    参考代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define il inline
    #define ri register
    using namespace std;
    bool dp[100001];
    int a[101],c[101],w[1001],wt;
    il void read(int&);
    int main(){
        int n,m,i,j,k,ans;
        while(read(n),read(m),n&&m){
            memset(dp,0,sizeof(dp)),wt&=0;
            for(i=1;i<=n;++i)read(a[i]);
            for(i=1;i<=n;++i)read(c[i]);
            for(i=1;i<=n;++i){
                j=1;
                while(c[i]-j>=0){
                    w[++wt]=a[i]*j;
                    c[i]-=j,j<<=1;
                }w[++wt]=a[i]*c[i];
            }dp[0]|=true,ans&=0;
            for(i=1;i<=wt;++i)
                for(j=m;j>=w[i];--j)
                    dp[j]|=dp[j-w[i]];
            for(i=1;i<=m;++i)ans+=dp[i];
            printf("%d
    ",ans);
        }
        return 0;
    }
    il void read(int &x){
        x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    
    

    法二:阶段内转移优化

    此题关注的是可行性,而多次选择一个硬币类似完全背包,考虑阶段内转移优化,而设(dp[j])为前i种硬币,是否组成钱为j,而(u[j])表示组成钱j的最少需要钱i的个数。

    于是当这个钱已经能组成,当然无需再组成,当这里没有组成,前面已经有组成的方案,比较前面的使用的该种硬币的个数,是否满足题意,即是否超越已用硬币数,之所以可行,是因为传统多重背包要求了权值最大,而使用的物品最少并不能保证权值最大,本题要求可行性,于是我们只要硬币数用的足够少,来保证组成尽可能多的硬币,于是仿照完全背包,顺序枚举

    [dp[j]|=true(!dp[j]&&dp[j-a_i]&&u[j-a_i]<c_i) ]

    边界:(dp[0]=1)

    答案:枚举累加有1的数即可

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define il inline
    #define ri register
    using namespace std;
    bool dp[100001];
    int used[100001],a[101],c[101];
    il void read(int&);
    int main(){
        int n,m;while(read(n),read(m),n&&m){
            for(ri int i(1);i<=n;++i)read(a[i]);
            for(ri int i(1);i<=n;++i)read(c[i]);
            memset(dp,0,sizeof(dp)),dp[0]|=true;
            for(ri int i(1),j;i<=n;++i){
                memset(used,0,sizeof(used));
                for(j=a[i];j<=m;++j)
                    if(used[j-a[i]]<c[i]&&dp[j-a[i]]&&!dp[j])
                        dp[j]|=true,used[j]=used[j-a[i]]+1;
            }int ans(0);
            for(ri int i(1);i<=m;++i)ans+=dp[i];
            printf("%d
    ",ans);
        }
        return 0;
    }
    il void read(int &x){
        x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    
    
  • 相关阅读:
    使用hugo在gitee上写blog
    golang初识2
    golang初识1
    install go on ubuntu
    sql优化的几种方式
    UpdatePanel 无刷新弹出窗口
    .net web 点击链接在页面指定位置显示DIV的问题
    重建主键
    sql 日期时间格式转换
    UpdatePanel无法直接弹出窗口的解决
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/10917593.html
Copyright © 2020-2023  润新知