bzoj1042硬币购物

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1042

dp预处理+容斥原理。

先预处理求出无限制的各面值的组成方案数 f (完全背包)。

求s [ i ]有限制的,就是s [ i ]无限制方案数 - 单种硬币一定超过限制的方案数 + 两种的 - 三种的 + 四种的。

第 k 中硬币一定超过限制的方案数就是f [ s [ i ] - c [ k ] * ( d [ k ] + 1 ) ],即确定用了 d + 1 个该硬币,刨去它们后的无限制方案数。

当 c [ k ] * ( d [ k ] + 1 ) > s [ i ] 时不用操作,即没有“超过该限制”的可能。但== s [ i ] 时还是要操作的,f [ 0 ] = 1。

以为要用高精度,结果WA。看看题解发现不用高精度,于是……

如果高精度的话,防止某次减到了负数,可以先把加的弄了(反正只有四种硬币)。

非高精AC代码:

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
ll c[5],d[1005][5],tot,mx,s[1005],f[100005],ans;
void pre()
{
    f[0]=1;///
    for(int i=1;i<=4;i++)
        for(ll j=c[i];j<=mx;j++)
            f[j]+=f[j-c[i]];
}
void dfs(ll now,ll k,ll cnt,ll z)//第now次询问,第k号,选了cnt个,f目前脚标 
{
    if(z<0)return;//不是z<=0!! 
    if(k==5)
    {
        if(cnt&1)ans-=f[z];
        else if(cnt)ans+=f[z];
        return;
    }
    dfs(now,k+1,cnt,z);
    dfs(now,k+1,cnt+1,z-c[k]*d[now][k]-c[k]);
}
int main()
{
    for(int i=1;i<=4;i++)scanf("%lld",&c[i]);
    scanf("%lld",&tot);
    for(int i=1;i<=tot;i++)
        scanf("%lld%lld%lld%lld%lld",&d[i][1],&d[i][2],&d[i][3],&d[i][4],&s[i]),mx=max(mx,s[i]);
    pre();
    for(int i=1;i<=tot;i++)
    {
        ans=f[s[i]];
        dfs(i,1,0,s[i]);
        printf("%lld\n",ans);
    }
    return 0;
}

高精WA代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int INF=100005;
ll c[5],d[1005][5],tot,s[1005],mx,tp;
int f[INF][205],ans[205];
void plu(ll k,int a[],int b[])
{
    int tmp[205]={0},lm=max(a[0],b[0]);
    for(int i=1;i<=lm;i++)
    {
        tmp[i]+=a[i]+b[i];
        if(tmp[i]>10)tmp[i]-=10,tmp[i+1]++;
    }
    tmp[0]=lm;
    if(tmp[tmp[0]+1])tmp[0]++;
    memcpy(f[k],tmp,sizeof tmp);
}
void pre()
{
    f[0][1]=1;f[0][0]=1;
    for(int i=1;i<=4;i++)
        for(ll j=c[i];j<=mx;j++)
            plu(j,f[j],f[j-c[i]]);
}
void print()
{
//    printf("(%d)",ans[0]);
    for(int i=ans[0];i;i--)
        printf("%d",ans[i]);
    printf("\n");
}
void plu2(ll k)
{
    int lm=max(ans[0],f[k][0]);
    for(int i=1;i<=lm;i++)
    {
        ans[i]+=f[k][i];
        if(ans[i]>10)ans[i]-=10,ans[i+1]++;
    }
    if(ans[ans[0]+1])ans[0]++;
//    printf("++ k=%d ",k);print();
}
void jian(ll k)
{
//    int lm=max(ans[0],f[k][0]);
    for(int i=1;i<=f[k][0];i++)
    {
        ans[i]-=f[k][i];
        if(ans[i]<0)ans[i]+=10,ans[i+1]--;
    }
    while(!ans[ans[0]]&&ans[0]>1)ans[0]--;
//    printf("-- k=%d ",k);print();
}
//void debugprint()
//{
//    for(int i=1;i<=s[1];i++)
//    {
//        printf("i=%d ",i);
//        for(int j=f[i][0];j;j--)
//            printf("%d",f[i][j]);
//        printf("\n");
//    }
//}
int main()
{
    for(int i=1;i<=4;i++)scanf("%lld",&c[i]);
    scanf("%lld",&tot);
    for(int i=1;i<=tot;i++)
    {
        scanf("%lld%lld%lld%lld%lld",&d[i][1],&d[i][2],&d[i][3],&d[i][4],&s[i]);
        mx=max(mx,s[i]);
    }
    pre();
    for(int i=1;i<=tot;i++)
    {
        memcpy(ans,f[s[i]],sizeof f[s[i]]);//
//        debugprint();
        for(int u=1;u<4;u++)
            for(int v=u+1;v<=4;v++)
            {
                tp=c[u]*d[i][u]+c[v]*d[i][v]+c[u]+c[v];
                if(tp<=s[i])plu2(s[i]-tp);        ///<=!!!,因为我的f[0]有值为1 ;正是要用这个1! 
            }
        tp=c[1]*d[i][1]+c[2]*d[i][2]+c[3]*d[i][3]+c[4]*d[i][4]+c[1]+c[2]+c[3]+c[4];
        if(tp<=s[i])plu2(s[i]-tp);
        for(int u=1;u<=4;u++)
        {
            tp=c[u]*d[i][u]+c[u];
            if(tp<=s[i])jian(s[i]-tp);
        }
        for(int u=1;u<=2;u++)
            for(int v=u+1;v<=3;v++)
                for(int j=v+1;j<=4;j++)
                {
                    tp=c[u]*d[i][u]+c[v]*d[i][v]+c[j]*d[i][j]+c[u]+c[v]+c[j];
                    if(tp<=s[i])jian(s[i]-tp);
                }
        print();
    }
    return 0;
}

 

posted on 2018-03-08 22:24  Narh  阅读(...)  评论(... 编辑 收藏

导航