硬币购物

Posted lcan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了硬币购物相关的知识,希望对你有一定的参考价值。

技术分享图片

 

如果是背包的话

技术分享图片

 

这个时间复杂度不对

 

所以是容斥????

 

直接计算方案数不好计算,所以考虑补集转化,总共的方案数可以用完全背包预处理,就是设f[0]=1,以后不断+=就可以了,

那么不合法的方案呢,就是某些物品超出了数量限制。有可能一个超出限制,也有可能两个超出限制,所以是容斥

因为四个物品价值数量都不同,所以应该分开讨论

一个超出的有四种

两个超出的有六种......

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 const int maxn=1e5+7;
 8 typedef long long ll;
 9 ll t,s;
10 ll c[7],d[7],f[maxn];
11 int main(){
12   cin>>c[1]>>c[2]>>c[3]>>c[4]>>t;
13   f[0]=1;
14   for(int i=1;i<=4;i++)
15     for(int j=c[i];j<=maxn-5;j++)
16       f[j]+=f[j-c[i]];
17   while(t--){
18     cin>>d[1]>>d[2]>>d[3]>>d[4]>>s;
19     ll ret=f[s];
20     if(s>=(d[1]+1)*c[1]) ret-=f[s-(d[1]+1)*c[1]];
21     if(s>=(d[2]+1)*c[2]) ret-=f[s-(d[2]+1)*c[2]];
22     if(s>=(d[3]+1)*c[3]) ret-=f[s-(d[3]+1)*c[3]];
23     if(s>=(d[4]+1)*c[4]) ret-=f[s-(d[4]+1)*c[4]];
24     if(s>=(d[1]+1)*c[1]+(d[2]+1)*c[2]) ret+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]];
25     if(s>=(d[1]+1)*c[1]+(d[3]+1)*c[3]) ret+=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]];
26     if(s>=(d[1]+1)*c[1]+(d[4]+1)*c[4]) ret+=f[s-(d[1]+1)*c[1]-(d[4]+1)*c[4]];
27     if(s>=(d[2]+1)*c[2]+(d[3]+1)*c[3]) ret+=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]];
28     if(s>=(d[2]+1)*c[2]+(d[4]+1)*c[4]) ret+=f[s-(d[2]+1)*c[2]-(d[4]+1)*c[4]];
29     if(s>=(d[3]+1)*c[3]+(d[4]+1)*c[4]) ret+=f[s-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
30     if(s>=(d[1]+1)*c[1]+(d[2]+1)*c[2]+(d[3]+1)*c[3]) ret-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]];
31     if(s>=(d[1]+1)*c[1]+(d[2]+1)*c[2]+(d[4]+1)*c[4]) ret-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[4]+1)*c[4]];
32     if(s>=(d[1]+1)*c[1]+(d[3]+1)*c[3]+(d[4]+1)*c[4]) ret-=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
33     if(s>=(d[2]+1)*c[2]+(d[3]+1)*c[3]+(d[4]+1)*c[4]) ret-=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
34     if(s>=(d[1]+1)*c[1]+(d[2]+1)*c[2]+(d[3]+1)*c[3]+(d[4]+1)*c[4])
35       ret+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
36     cout<<ret<<endl;
37   } 
38 }

还有一种更加简单的,可以用二进制数来枚举哪些物品超出了限制:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 typedef long long ll;
 8 const int maxn=1e5+7;
 9 ll c[7],d[7],f[maxn];
10 ll t,s,ans;
11 int main(){
12   cin>>c[1]>>c[2]>>c[3]>>c[4]>>t;
13   f[0]=1;
14   /*for(ll i=1;i<=maxn-5;i++)
15     for(ll j=1;j<=4;j++)
16       if(i>=c[j]) f[i]+=f[i-c[j]];*/
17   for(int i=1;i<=4;i++)
18     for(int j=c[i];j<=maxn-5;j++)
19       f[j]+=f[j-c[i]];
20   while(t--){
21     cin>>d[1]>>d[2]>>d[3]>>d[4]>>s;
22     ans=0;
23     for(ll i=0;i<=15;i++){
24       ll ret=s,cnt=0;
25       for(ll j=1;j<=4;j++){
26         if((i>>(j-1))&1){
27           cnt^=1;ret-=c[j]*(d[j]+1);
28         }
29       }
30       if(ret<0) continue;
31       if(cnt) ans-=f[ret];
32       else ans+=f[ret];
33     }
34     cout<<ans<<endl;
35   }
36 } 

背包的枚举顺序啊

 

以上是关于硬币购物的主要内容,如果未能解决你的问题,请参考以下文章

[HAOI2008]硬币购物

BZOJ 1042: [HAOI2008]硬币购物

bzoj 1042: [HAOI2008]硬币购物

BZOJ1042[HAOI2008]硬币购物 容斥

Luogu P1450 [HAOI2008]硬币购物

HAOI2008硬币购物