●BZOJ 1042 [HAOI2008]硬币购物

题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=1042

题解:

容斥原理,dp预处理
首先跑个无限物品的背包dp求出dp[i]表示在四种物品都有无限个情况下有多少种方法支付 i元。
然后对于每个询问,答案就是 dp[S]-不合法的方法。
那么这个不合法的方法数怎么求呢?
举个例子:如果 c1不能超过d1个的话,那么我们就强制用掉 d1+1个 c1硬币,
那么dp[S-(d1+1)*c1]就是c1不合法的方法数。

所以这样就可以类似的求出其它硬币的不合法的方法数,以及某几种硬币都不合法的方法数,用于容斥计算。
即 ANS=dp[S] - 一种硬币不合法 + 两种硬币不合法 -三种硬币不合法 +四种硬币不合法。
DFS实现

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 105000
#define ll long long
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
ll dp[MAXN],c[10],d[10];
ll tot,ANS,S;
void dfs(int p,int num,ll de){
	if(p==5) return;
	ll nde=de+(d[p]+1)*c[p];
	ll val=S-nde<0?0:dp[S-nde];
	ANS+=val*(((num+1)&1)?-1:1);
	dfs(p+1,num+1,nde);
	dfs(p+1,num,de);
}
int main()
{
	dp[0]=1;
	for(int i=1;i<=4;i++) {
		scanf("%lld",&c[i]);
		for(int j=c[i];j<=100000;j++)
			dp[j]+=dp[j-c[i]];
	}
	scanf("%lld",&tot);
	while(tot--){
		for(int i=1;i<=4;i++) 
			scanf("%lld",&d[i]);
		scanf("%lld",&S);
		ANS=dp[S];
		dfs(1,0,0);
		printf("%lld\n",ANS);
	}
	return 0;
}

posted @ 2017-12-12 19:27  *ZJ  阅读(...)  评论(... 编辑 收藏