首页 写随笔

cdcq(本博客废弃!现用博客:https://www.cnblogs.com/cdcq/)

本博客废弃!现用博客:https://www.cnblogs.com/cdcq/

导航

【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 }
View Code

 

posted on 2017-03-05 18:26  cdcq_old  阅读(214)  评论(0编辑  收藏  举报