首页 > 代码库 > DP+容斥 BZOJ1042

DP+容斥 BZOJ1042

1042: [HAOI2008]硬币购物

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2558  Solved: 1539
[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
 
佩服脑洞 http://www.cnblogs.com/zhuohan123/p/3284272.html  
 
裸01背包(包括二进制分解),会造成重复计数 比如两个2元硬币 造成dp[2]=2。
也就是说同样数目的同种硬币本质是一样的,却重复计数了
如果每种硬币数目都是2^n-1的形式,应该就可以二进制分解了,但是这题显然不是=_=
#include<cstdio>
typedef long long ll;
int c[5];
ll dp[110000];
struct{long long operator[](const int &pos){return pos<0?0:dp[pos];}}f;
int main(){
   int t;
   scanf("%d%d%d%d%d",c+1,c+2,c+3,c+4,&t);
   dp[0]=1;
   for(int i=1;i<=4;++i)
    for(int j=0;j<=100000;++j)
    if(j+c[i]<=100000) dp[j+c[i]]+=dp[j];
   while(t--){
    int d[5],s;
    scanf("%d%d%d%d%d",d+1,d+2,d+3,d+4,&s);
    long long ans=f[s];
    ans-=f[s-(d[1]+1)*c[1]];
    ans-=f[s-(d[2]+1)*c[2]];
    ans-=f[s-(d[3]+1)*c[3]];
    ans-=f[s-(d[4]+1)*c[4]];
    ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]];
    ans+=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]];
    ans+=f[s-(d[1]+1)*c[1]-(d[4]+1)*c[4]];
    ans+=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]];
    ans+=f[s-(d[2]+1)*c[2]-(d[4]+1)*c[4]];
    ans+=f[s-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
    ans-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]];
    ans-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[4]+1)*c[4]];
    ans-=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
    ans-=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
    ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
    printf("%lld\n",ans);
   }
}

DP+容斥 BZOJ1042