[HAOI2008] 硬币购物
08年是让我不淡定的一年。
这题我是完全没思路。
正解是容斥原理。
1:预处理完全背包,也就每种硬币可以使用无限次组成面值 S 的方案数。
2:ans = 总方案 - ( c1 超 + c2 超 + c3 超 + c4 超 ) + ( c1,c2 超 + c2,c3 超 +c3,c4 超 + c1,c4超) - ......
实现起来有用位运算实现的,但是 BYVoid 的递归实现比较好理解也更清晰一点。
// q.c // *** #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; const int M=100000; int c[5],d[5],tot,s; long long f[M+10],ans; void dfs(int x,int k,int sum) { // x表示第几种硬币,k表示几个超过限定数量的. if(sum<0) return ; // 这里是严格小于0. if(x==5) { if(k&1) ans-=f[sum]; // 总方案减去不符合条件的,偶加奇减. else ans+=f[sum]; return ; } dfs(x+1,k+1,sum-(d[x]+1)*c[x]); // 第x种硬币超过限定额度. dfs(x+1,k,sum); } int main() { freopen("coin.in","r",stdin); freopen("coin.out","w",stdout); for(int i=1;i<=4;i++) scanf("%d",&c[i]); f[0]=1; for(int i=1;i<=4;i++) for(int j=c[i];j<=M;j++) f[j]+=f[j-c[i]]; scanf("%d",&tot); while(tot--) { for(int i=1;i<=4;i++) scanf("%d",&d[i]); scanf("%d",&s); ans=0; dfs(1,0,s); printf("%lld\n",ans); } return 0; }