POJ1742:Coins

浅谈\(DP\)https://www.cnblogs.com/AKMer/p/10437525.html

题目传送门:http://poj.org/problem?id=1742

多重背包,每个物品可以使用若干次的背包,我们只需要多枚举一次当前要使用多少次然后把这么多次结合在一起,当做\(01\)背包做即可。复杂度\(O(\sum cnt_im)\)\(cnt_i\)表示第\(i\)个物品的个数

二进制分解优化:

因为每个数必然可以被分解成若干个二进制数相加,所以我们就把\(cnt_i\)提前分解为若干个二进制数,把这么多个物品先打包成一个物品,做一遍\(01\)背包必然包含了选择任意个该物品的决策。

时间复杂度:\(O(\sum log(cnt_i)m)\)

空间复杂度:\(O(m)\)

代码如下:

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn=1e5+5;

bool f[maxn];
int n,m,tot,ans;
int a[maxn],c[maxn],coin[maxn];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

int main() {
    while(1) {
        n=read(),m=read(),tot=ans=0;
        if(!n&&!m)break;
        memset(f,0,sizeof(f)),f[0]=1;
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=n;i++)c[i]=read();
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=512;j*=2)
                if(c[i]>j)coin[++tot]=a[i]*j,c[i]-=j;
            if(c[i])coin[++tot]=c[i]*a[i];
        }
        for(int i=1;i<=tot;i++)
            for(int j=m;j>=coin[i];j--)
                f[j]|=f[j-coin[i]];
        for(int i=1;i<=m;i++)ans+=f[i];
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/AKMer/p/10437687.html