poj1742 Coins

题目描述

题解:

这是个多重背包,但是一般的复杂度是过不去这题的。

所以有二进制优化和单调队列优化。

二进制优化是将数量$n$化为多个数,而且这些数能表示出$1~n$中的任意数。

怎么保证?

想起二进制,我们可以将$n$分为$1+2+4+8+……+k$,$k$可以是任意数。

单调队列怎么优化?

我们发现,转移时$f[i]$的状态可由$f[i-a*k]$转移而来,于是对于$0~a-1$进行枚举,每次更新与之同余的所有数。

单调队列代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 105
#define M 100050
int n,m,a[N],c[N];
bool f[M];
int sta[M],hd,tl;
int main()
{
    while(scanf("%d%d",&n,&m))
    {
        if(!n&&!m)break;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        memset(f,0,sizeof(f));
        f[0]=1;
        for(int i=1;i<=n;i++)
        {
            if(c[i]==1)
            {
                for(int j=m;j>=a[i];j--)
                    if(f[j-a[i]])f[j]=1;
            }else if(a[i]*c[i]>=m)
            {
                for(int j=a[i];j<=m;j++)
                    if(f[j-a[i]])f[j]=1;
            }else
            {
                for(int j=0;j<a[i];j++)
                {
                    hd=1,tl=0;
                    for(int k=j;k<=m;k+=a[i])
                    {
                        while(hd<=tl&&sta[hd]+c[i]*a[i]<k)hd++;
                        if(!f[k])
                        {
                            if(hd<=tl)f[k]=f[sta[hd]];
                        }else
                        {
                            sta[++tl]=k;
                        }
                    }
                }
            }
        }
        int ans = 0;
        for(int i=1;i<=m;i++)ans+=f[i];
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LiGuanlin1124/p/10206818.html