【HAOI2008】硬币购物
容斥妙啊
原题:
一共有4种硬币,面值分别为c1,c2,c3,c4. 阿Q带着一些硬币去商店买东西,他带了d1枚第一种硬币,d2枚第二种硬币,d3枚第三种硬币,d4枚第四种硬币,若想买一个价值为s的东西,问阿Q有多少种付coins的方法.
比如c={1,2,5,10},d={3,2,3,1},s=10,一共有4种方法:
10=1+1+1+2+5
10=1+2+2+5
10=5+5
10=10
注意,阿Q可能会去很多次商店,每次带的硬币数量和要买的东西价值可能不一样,你需要对每一次都求出方法总数。
d1,d2,d3,d4,s <=100000,tot<=1000
志己想不出来,看题解
先完全背包求没有限制的方案数
然后容斥
容斥什么呐
容斥f[s]-Σ一种硬币超限度的方案数+Σ两种硬币超限度的方案数-Σ三种+Σ四种-Σ五种
怎么算一种硬币超限度的方案数呐
为了让第i种硬币超限度,就先钦定已经使用了d[i]+1个,酱紫这个硬币已经超了,接下来这种硬币是否再用就无所谓了
所以一种硬币超限度的方案数就是f[s-(d[i]+1)*c[i]]
dfs搞一搞就行了
题解和代码写起来都非常简单
然而思路在本身在考场上非常难想出来啊
怎么办嘛QAQ
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define ll long long 8 void splay(int &z){ z=0; int mark=1; char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')mark=-1; ch=getchar();} 10 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 11 } 12 int dinic(){int z=0,mark=1; char ch=getchar(); 13 while(ch<'0'||ch>'9'){if(ch=='-')mark=-1; ch=getchar();} 14 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 15 return z*mark; 16 } 17 int n; 18 int cost[5],num[5]; 19 ll f[110000],ans; 20 void dfs(int x,int y,int z){ 21 if(x==5){ ans+=(y&1 ? -1 : 1)*f[z]; return ;} 22 if((num[x]+1)*cost[x]<=z) dfs(x+1,y+1,z-(num[x]+1)*cost[x]); 23 dfs(x+1,y,z); 24 } 25 int main(){//freopen("ddd.in","r",stdin); 26 for(int i=1;i<=4;++i) cin>>cost[i]; 27 cin>>n; 28 f[0]=1; 29 for(int i=1;i<=4;++i)for(int j=cost[i];j<=100000;++j) 30 f[j]+=f[j-cost[i]]; 31 while(n--){ 32 for(int i=1;i<=4;++i) splay(num[i]); 33 ans=0; 34 dfs(1,0,dinic()); 35 printf("%I64d\n",ans); 36 } 37 return 0; 38 }