BZOJ1042:硬币购物(背包 & 容斥)

1042: [HAOI2008]硬币购物

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 2953   Solved: 1822
[ Submit][ Status][ Discuss]

Description

  硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。

Input

  第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000

Output

  每次的方法数

Sample Input

1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900

Sample Output

4
27

HINT

Source

思路:很巧妙的题目,一般我们是直接完全背包那样求方案数的,但是这样会超时,考虑到硬币种类不多,应该先算出不限硬币数目的方案数,再利用容斥原理去掉非法的方案数,b[i]+1就是第i种硬币非法的状态了。

# include <iostream>
# include <cstdio>
using namespace std;
typedef long long LL;
const int maxn = 1e5;
LL dp[maxn+30]={1}, a[5], b[5], s, ans;
void dfs(int pos, LL sum, int cnt)
{
    if(sum < 0) return;
    if(pos == 5)
    {
        if(cnt&1) ans -= dp[sum];
        else ans += dp[sum];
        return;
    }
    dfs(pos+1, sum, cnt);
    dfs(pos+1, sum-(b[pos]+1)*a[pos], cnt+1);
}
int main()
{
    int t;
    for(int i=1; i<=4; ++i) scanf("%lld",&a[i]);
    for(int i=1; i<=4; ++i)
        for(int j=0; j+a[i]<=maxn; ++j)
            dp[j+a[i]] += dp[j];
    scanf("%d",&t);
    while(t--)
    {
        for(int i=1; i<=4; ++i) scanf("%lld",&b[i]);
        scanf("%lld",&s);
        ans = 0;
        dfs(1, s, 0);
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值