[bzoj1042] [HAOI2008]硬币购物

[bzoj1042] [HAOI2008]硬币购物

Description

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

Input

第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s

Output

每次的方法数

HINT

数据规模
di,s<=100000
tot<=1000

第一眼看过去,这是一道求背包方案数的问题,这个我们大家都会求.但是题目显然不会有那么简单.第一眼数据范围感觉像是\(O(N)\)的,对于每次询问\(O(1)\)出解,这样感觉差不多.好了,我们的思路回到如何如何预处理上去.由于C1,C2,C3,C4是提前告诉你了的,但由于D1,D2,D3,D4,s没有告诉你.所以我们无法根据询问处理出答案.但是我们能处理出没有限制的硬币数情况下的方案总数.所以我们考虑由非限制硬币数的答案转移到限制的硬币数的答案.于是我们想到了容斥原理.我们算出了所有的非限制硬币数的答案,那么我们还可以算出每个硬币超限的方案数,那么我们就可以通过容斥原理来实现了.那么答案就是:总超限购买的方案数[sum]-硬币1超限购买的方案数...+硬币1和硬币2超限购买的方案数...最后我们考虑每一个硬币超限购买的方案数.假设是硬币x,我们优先用D[x]+1个硬币x,刚好超限.假设当前算到的剩余钱数是sum,那么方案就是f[sum-(D[x]+1)*C[x]].具体过程通过dfs实现方便一些.

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long LL;

static const int maxm=5;
static const int size=1e6+10;

int D[maxm],C[maxm];
LL f[size];
int T,tot;
LL ans;

void dfs(int x,int k,int sum){
    if(sum<0)return;
    if(x==5){
        if(k&1)ans-=f[sum];
        else ans+=f[sum];
        return;
    }
    dfs(x+1,k,sum);
    dfs(x+1,k+1,sum-(D[x]+1)*C[x]);
}

int main(){
    for(int i=1;i<=4;i++)scanf("%d",&C[i]);
    scanf("%d",&T);
    
    f[0]=1;
    for(int i=1;i<=4;i++)
        for(int v=C[i];v<=100000;v++)
            f[v]+=f[v-C[i]];
    
    while(T--){
        for(int i=1;i<=4;i++)scanf("%d",&D[i]);
        scanf("%d",&tot);ans=0;
        dfs(1,0,tot);
        printf("%lld\n",ans);
    }
    
    
    return 0;
}

传送门

posted @ 2017-05-06 19:17  Exbilar  阅读(...)  评论(... 编辑 收藏