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

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;
}


你可能感兴趣的:(BZOJ 1042: [HAOI2008]硬币购物 (dp + 容斥))