容斥原理是基本的计数方法。在计数时,我们要做到没有遗漏也没有重复,所以我们推出了一下公式:
如果被计数的事物有A、B两类,那么,A类B类元素个数总和= 属于A类元素个数+ 属于B类元素个数—既是A类又是B类的元素个数。(A∪B = A+B - A∩B)
我比较懒,后面的情况自己推吧……(*^__^*) 嘻嘻……
下面给一道例题:
硬币购物 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
现在一共有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,所给数据保证每次至少有一种付款方法。
|
首先dp预处理设F[i]为不考虑每种硬币的数量限制的情况下,得到面值i的方案数。则状态转移方程为
F[i]=Sum{F[i-C[k]] | i-C[k]>=0 且 k=1..4}
然后容斥原理
代码如下
1 #include<iostream> 2 using namespace std; 3 int c[5],d[5],tot; 4 long long f[100001],ans; 5 void work(int now,int num,int s) 6 { 7 if(s<0)return ; 8 if(now==5) 9 { 10 if(num%2==1) ans-=f[s]; 11 else ans+=f[s]; 12 return ; 13 } 14 work(now+1,num,s); 15 work(now+1,num+1,s-(d[now]+1)*c[now]); 16 } 17 int main() 18 { 19 scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&tot); 20 f[0]=1; 21 for(int i=1;i<=4;i++) 22 for(int j=c[i];j<=100000;j++) f[j]+=f[j-c[i]]; 23 for(int i=1;i<=tot;i++) 24 { 25 int s; 26 scanf("%d%d%d%d%d",&d[1],&d[2],&d[3],&d[4],&s); 27 ans=0; 28 work(1,0,s); 29 if(i==1) printf("%lld",ans); 30 else printf(" %lld",ans); 31 } 32 return 0; 33 }