• 硬币购物


    2016.1.27

    试题描述

    现在一共有4种硬币,面值各不相同,分别为ci(i=1,2,3,4)。某人去商店买东西,去了tot次,每次带di枚ci硬币,购买价值为si的货物。请问每次有多少种付款方法。

    输入
    第一行包括五个数,分别为c1,c2,c3,c4和tot 接下来有tot行,每行五个数,第i+1行五个数依次为第i次购物所带四种硬币的数目和购买货物的价值(d1,d2,d3,d4,s )。各行的数两两之间用一个空格分隔。
    输出
    一行,包括tot个数,依次为每次付款的方法数。两数之间用一个空格分隔。
    输入示例
    1 2 5 10 2
    3 2 3 1 10
    1000 2 2 2 900
    输出示例
    4 27
    其他说明
    数据范围:0<di,s<=100000,0<tot<=1000,数据面值最大不超过20,所给数据保证每次至少有一种付款方法。

    hzwer给的容斥原理解法

    先预处理出达到面值s的方法,不考虑硬币是否超过限制。这个用简单背包就好。

    然后从里面扣出有至少一枚硬币超过限制的方法即是答案。

    又有:至少一枚硬币超限=第一种硬币超限+第二种硬币超限+第三种硬币超限+第四种硬币超限-第一种和第二种都超限-第一种和第三种都超限-第一种和第四种都超限-第二种和第三种都超限-第二种和第四种都超限-第三种和第四种都超限+......-四种都超限

    然后就状压容斥

    若第一种硬币超限,则f[s-(d[1]+1)*c[1]]即为方案数,因为保证了多用一次第一种硬币。以此类推

    注意判sum超s的情况

    AC代码:

    #include<iostream>
    using namespace std;
    int c[5],tot,d[5],s,ct,sum,flag;
    long long f[100005],ans;
    int main()
    {
        for(int i=1;i<=4;i++) scanf("%d",&c[i]);
        scanf("%d",&tot);
        f[0]=1;
        for(int i=1;i<=4;i++)
        {
            for(int j=c[i];j<=100000;j++)
            {
                f[j]+=f[j-c[i]];
            }
        }
        while(tot--)
        {
            for(int i=1;i<=4;i++) scanf("%d",&d[i]);
            scanf("%d",&s);
            ans=0;
            for(int i = (1 << 4) - 1 ; i >= 0 ; i-- )
            {
                ct=0;sum=0;
                for(int j = 3 ; j >= 0 ; j-- )
                {
                    if(1<<j & i) ct++,sum+=(d[j+1]+1)*c[j+1]; 
                } 
                if(s<sum) continue;
                if(ct & 1) ans-=f[s-sum];
                else ans+=f[s-sum];
            }
            if(flag) cout<<" ";
            flag=1;
            cout<<ans;
        }
    }
    View Code
  • 相关阅读:
    计算机网络基础
    ansible
    CDH集群日常
    漏洞挖掘学习
    JDWP
    开源安全项目调研
    SMB漏洞汇总
    Windows账户LM/NTLM
    SMB知识汇总
    Memcache未授权漏洞
  • 原文地址:https://www.cnblogs.com/16er/p/5163514.html
Copyright © 2020-2023  润新知