导航

硬币购物

Posted on 2016-01-27 15:45  POOH1DROSE  阅读(368)  评论(0编辑  收藏  举报

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