BZOJ 1042: [HAOI2008]硬币购物

题目地址:http://www.lydsy.com/JudgeOnline/problem.php?id=1042

题目大意:4种硬币,面值分别为c1,c2,c3,c4,每种硬币分别有d1,d2,d3,d4枚,买价值为s的东西,有几种付款方法。

算法讨论:

        由于做多重背包会超时,考虑其他算法。

        首先做完全背包,设f[i]表示面值为i的方案数,转移方程为f[i]=sigma(f[i-cj])。

        然后对4种硬币进行容斥,强制某些硬币超过限制,取di+1枚,对剩余的硬币进行分配。

Code:

#include 

#define N 100000

using namespace std;

bool v[5];
int T,s,c[5],d[5];
long long ans,f[N+10];

inline void dp(){
	f[0]=1;
	for (int i=1;i<=4;++i)
		for (int j=0;j<=100000-c[i];++j) f[j+c[i]]+=f[j];
}

void dfs(int k,int cnt){
	if (k>4){
		long long t=s;
		for (int i=1;i<=4;++i)
			if (v[i]) t-=(long long)(d[i]+1)*c[i];
		if (t>=0) if (cnt&1) ans-=f[t];else ans+=f[t];
		return;
	}
	v[k]=0;
	dfs(k+1,cnt);
	v[k]=1;
	dfs(k+1,cnt+1);
}

int main(){
	#ifndef ONLINE_JUDGE
	freopen("1042.in","r",stdin);
	freopen("1042.out","w",stdout);
	#endif
	for (int i=1;i<=4;++i) scanf("%d",&c[i]);
	scanf("%d",&T);
	dp();
	while (T--){
		for (int i=1;i<=4;++i) scanf("%d",&d[i]);
		scanf("%d",&s);
		ans=0;
		dfs(1,0);
		printf("%lld\n",ans);
	}
	return 0;
}


你可能感兴趣的:(BZOJ,BZOJ,容斥,DP,完全背包)