BZOJ 1042: [HAOI2008]硬币购物 (dp + 容斥)

来源:互联网 发布:海岛旅游小镇数据 编辑:程序博客网 时间:2024/06/05 02:29

1042: [HAOI2008]硬币购物

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1809  Solved: 1056
[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

[Submit][Status][Discuss]


HOME Back

如果不考虑硬币个数,那么这题是一个简单的完全背包。

先用100W的复杂度预处理出dp  dp[i]表示达到i的方案数

然后考虑  对于第i硬币数目大于d[i]的方案数的不行的

对于这种情况 ans应该减去dp[s-(d[i]+1)*c[i]]

然后考虑去重 即用容斥  奇加偶减

即可得到答案

#include<stdio.h>#include<string.h>#include<iostream>using namespace std;const int N=10005;long long dp[1000005],ans;int c[5],tot,d[5],s;void dfs(int st,int hav,int sum){    if(st>4)    {        if(sum>s||hav==0) return ;        if(hav&1) ans-=dp[s-sum];        else ans+=dp[s-sum];        return ;    }    dfs(st+1,hav+1,sum+c[st]*(d[st]+1));    dfs(st+1,hav,sum);}int main(){    int i,j,q;    while(~scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&tot))    {        memset(dp,0,sizeof(dp));        dp[0]=1;        for(i=1;i<=4;i++)            for(j=c[i];j<=1000000;j++)                dp[j]+=dp[j-c[i]];        while(tot--)        {            for(i=1;i<=4;i++) scanf("%d",&d[i]);            scanf("%d",&s);            ans=dp[s];            dfs(1,0,0);            cout<<ans<<endl;        }    }    return 0;}


0 0