Description
硬币購物一共有4種硬币。面值分别為c1,c2,c3,c4。某人去商店買東西,去了tot次。每次帶di枚ci硬币,買si的價值的東西。請問每次有多少種付款方法。
Input
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s
Output
每次的方法數
Sample Input
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
HINT
資料規模
di,s<=100000
tot<=1000
題解:
dp預處理+容斥原理
先完全背包來一次。
為避免方案重複,要以k為階段遞推,邊界條件為F[0]=1,這樣預處理的時間複雜度就是O(S)。
接下來對于每次詢問,奇妙的解法如下:根據容斥原理,答案為 得到面值S的超過限制的方案數 – 第1種硬币超過限制的方案數 – 第2種硬币超過限制的方案數 – 第3種硬币超過限制的方案數 – 第4種硬币超過限制的方案數 + 第1,2種硬币同時超過限制的方案數 + 第1,3種硬币同時超過限制的方案數 + …… + 第1,2,3,4種硬币全部同時超過限制的方案數。
當第1種硬币超過限制時,隻要要用到D[1]+1枚硬币,剩餘的硬币可以任意配置設定,是以方案數為 F[ S – (D[1]+1)C[1] ],當且僅當(S – (D[1]+1)C[1])>=0,否則方案數為0。其餘情況類似,每次詢問隻用問16次,是以詢問的時間複雜度為O(1)。
code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
//#include <queue>
using namespace std;
long long f[200010],ans;
int val[4],num[4];
void dfs(int x, int k, int sum);
int main()
{
int n;
for(int i=0; i<=3; i++){
scanf("%d",val+i);
}
scanf("%d",&n);
f[0]=1;
for(int i=0;i<=3;i++){
for(int j=val[i]; j<=100010;j++){
f[j]+=f[j-val[i]];
}
}
while(n--){
for(int i=0; i<=3; i++){
scanf("%d",num+i);
}
ans=0;
int _max;
cin>>_max;
dfs(0,0,_max);
printf("%lld\n",ans);
}
return 0;
}
void dfs(int x, int k, int sum)
{
if(sum<0)return ;
if(x==4){
if(k&1){
ans-=f[sum];
}else {
ans+=f[sum];
}
return;
}
dfs(x+1,k+1,sum-val[x]*(num[x]+1));
dfs(x+1,k,sum);
}