天天看點

1042: [HAOI2008]硬币購物(dp+容斥原理)

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);
}