Coins

Coins

有n个硬币,第i个硬币的价值\(a_i\),数量\(c_i\),现在求这些硬币组成的1~m以内的钱的个数,\(1<=n<=100,m<=100000,c_i\leq 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\n",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\n",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();
}

posted @ 2019-05-24 13:20  a1b3c7d9  阅读(363)  评论(0编辑  收藏  举报