[bzoj1042] [HAOI2008]硬币购物

Posted hbyer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj1042] [HAOI2008]硬币购物相关的知识,希望对你有一定的参考价值。

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

Solution

很巧妙的思想。

首先不考虑限制,那么做一遍完全背包就行了。

若只有第一个货币有限制,设只能用(d)个,面值为(c),那么买(s)元钱的方案就是(f[s]-f[s-ccdot (d+1)]),其中(f[i])是完全背包的东西,即不考虑限制的方案。

因为现在只能用(d)(c)元的钞票,那么一个比较显然的想法就是,由于所有超过(d)的方案都是不合法的,不妨用总体减去不合法的方案。

那么扩展到所有货币都有限制也就很显然了,由于货币种类较小,直接暴力容斥就好了。

时间复杂度(O(s+2^4cdot tot))

#include<bits/stdc++.h>
using namespace std;

#define int long long 

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}

const int maxn = 1e5+10;

int c[5],tot,f[maxn],d[5];

signed main() {
    for(int i=1;i<=4;i++) read(c[i]);read(tot);
    f[0]=1;
    for(int i=1;i<=4;i++)
        for(int j=0;j+c[i]<maxn;j++)
            f[j+c[i]]+=f[j];
    for(int w=1;w<=tot;w++) {
        int mx,ans=0;
        for(int i=1;i<=4;i++) read(d[i]);read(mx);
        for(int s=0;s<(1<<4);s++) {
            int a=mx;
            for(int i=1;i<=4;i++)
                if(s&(1<<(i-1))) a-=c[i]*(d[i]+1);
            if(a<0) continue;
            ans+=(__builtin_popcount(s)&1?-1:1)*f[a];
        }write(ans);
    }
    return 0;
}

以上是关于[bzoj1042] [HAOI2008]硬币购物的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1042[HAOI2008]硬币购物 容斥

bzoj 1042: [HAOI2008]硬币购物 dp+容斥原理

bzoj 1042: [HAOI2008]硬币购物

BZOJ-1042: [HAOI2008]硬币购物 (背包DP+容斥原理)

bzoj1042 HAOI2008—硬币购物

[bzoj1042] [HAOI2008]硬币购物