2016.1.27
试题描述
|
现在一共有4种硬币,面值各不相同,分别为ci(i=1,2,3,4)。某人去商店买东西,去了tot次,每次带di枚ci硬币,购买价值为si的货物。请问每次有多少种付款方法。 |
输入
|
第一行包括五个数,分别为c1,c2,c3,c4和tot 接下来有tot行,每行五个数,第i+1行五个数依次为第i次购物所带四种硬币的数目和购买货物的价值(d1,d2,d3,d4,s )。各行的数两两之间用一个空格分隔。
|
输出
|
一行,包括tot个数,依次为每次付款的方法数。两数之间用一个空格分隔。
|
输入示例
|
1 2 5 10 2
3 2 3 1 10 1000 2 2 2 900 |
输出示例
|
4 27
|
其他说明
|
数据范围:0<di,s<=100000,0<tot<=1000,数据面值最大不超过20,所给数据保证每次至少有一种付款方法。
|
hzwer给的容斥原理解法
先预处理出达到面值s的方法,不考虑硬币是否超过限制。这个用简单背包就好。
然后从里面扣出有至少一枚硬币超过限制的方法即是答案。
又有:至少一枚硬币超限=第一种硬币超限+第二种硬币超限+第三种硬币超限+第四种硬币超限-第一种和第二种都超限-第一种和第三种都超限-第一种和第四种都超限-第二种和第三种都超限-第二种和第四种都超限-第三种和第四种都超限+......-四种都超限
然后就状压容斥
若第一种硬币超限,则f[s-(d[1]+1)*c[1]]即为方案数,因为保证了多用一次第一种硬币。以此类推
注意判sum超s的情况
AC代码:
#include<iostream> using namespace std; int c[5],tot,d[5],s,ct,sum,flag; long long f[100005],ans; int main() { for(int i=1;i<=4;i++) scanf("%d",&c[i]); scanf("%d",&tot); f[0]=1; for(int i=1;i<=4;i++) { for(int j=c[i];j<=100000;j++) { f[j]+=f[j-c[i]]; } } while(tot--) { for(int i=1;i<=4;i++) scanf("%d",&d[i]); scanf("%d",&s); ans=0; for(int i = (1 << 4) - 1 ; i >= 0 ; i-- ) { ct=0;sum=0; for(int j = 3 ; j >= 0 ; j-- ) { if(1<<j & i) ct++,sum+=(d[j+1]+1)*c[j+1]; } if(s<sum) continue; if(ct & 1) ans-=f[s-sum]; else ans+=f[s-sum]; } if(flag) cout<<" "; flag=1; cout<<ans; } }