bzoj1042: [HAOI2008]硬币购物
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 #define maxn 100005 8 #define maxk 16 9 int c[5],tot,n,d[5],num[maxk]; 10 typedef long long ll; 11 ll f[maxn],ans; 12 void read(int &x){ 13 x=0; int f=1; char ch; 14 for (ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') f=-1; 15 for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; x*=f; 16 } 17 ll calc(int x,int y){ 18 for (int i=0;i<4;i++) if ((x>>i)&1) y-=c[i+1]*(d[i+1]+1); 19 if (y<0) return 0; 20 if (num[x]&1) return -f[y]; 21 else return f[y]; 22 } 23 int lowbit(int x){return x&(-x);} 24 int main(){ 25 for (int i=1;i<16;i++) num[i]=num[i-lowbit(i)]+1; 26 for (int i=1;i<=4;i++) read(c[i]); 27 memset(f,0,sizeof(f)),f[0]=1; 28 for (int i=1;i<=4;i++){ 29 for (int j=c[i];j<=100000;j++){ 30 f[j]+=f[j-c[i]]; 31 } 32 } 33 read(tot); 34 for (;tot;--tot){ 35 for (int i=1;i<=4;i++) read(d[i]); read(n); 36 ans=0; for (int i=0;i<16;i++) ans+=calc(i,n); 37 printf("%lld\n",ans); 38 } 39 return 0; 40 }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1042。
题目大意:硬币购物一共有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 每次的方法数。
做法:初看这题,对于每个询问,就是一个多重背包嘛,但是复杂度较高,过不了此题,我们发现这跟其他的背包有一点不同,就是物品只有4中,我们发现可以有指数级做法,想到了容斥原理,怎么容斥呢?显然,如果每个询问都没有限制,我们只需要O(n)的复杂度预处理,每次询问时便可以O(1)的回答了。但是怎么转化呢,这题中限制了上界不好转化,我们可以用容斥的一般式转化为补集,然后就变成了有部分变量规定了下界限制,我们在等式两边同时减去下界和即可,转化为了可以预处理出来的无限制的背包问题。容斥的注意事项,我们用一个4位的二进制数上各位的0/1来决定取还是不取,O(n)预处理出每个4位二进制数上1的个数,num[i]=num[i-lowbit(i)]+1,这很显然嘛。。
容斥原理+动态规划。