[HAOI2008] 硬币购物

  08年是让我不淡定的一年。

  这题我是完全没思路。

  正解是容斥原理。

  1:预处理完全背包,也就每种硬币可以使用无限次组成面值 S 的方案数。

  2:ans = 总方案 - ( c1 超 + c2 超 + c3 超 + c4 超 ) + ( c1,c2 超 + c2,c3 超 +c3,c4 超 + c1,c4超) - ......

  实现起来有用位运算实现的,但是 BYVoid 的递归实现比较好理解也更清晰一点。

 

// q.c
// ***

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int M=100000;
int c[5],d[5],tot,s;
long long f[M+10],ans;
void dfs(int x,int k,int sum) { // x表示第几种硬币,k表示几个超过限定数量的.
	if(sum<0) return ; // 这里是严格小于0.
	if(x==5) {
		if(k&1) ans-=f[sum]; // 总方案减去不符合条件的,偶加奇减.
		else ans+=f[sum];
		return ;
	}
	dfs(x+1,k+1,sum-(d[x]+1)*c[x]); // 第x种硬币超过限定额度.
	dfs(x+1,k,sum);
}
int main() {
	freopen("coin.in","r",stdin);
	freopen("coin.out","w",stdout);
	for(int i=1;i<=4;i++) scanf("%d",&c[i]);
	f[0]=1;
	for(int i=1;i<=4;i++)
		for(int j=c[i];j<=M;j++)
			f[j]+=f[j-c[i]];
	scanf("%d",&tot);
	while(tot--) {
		for(int i=1;i<=4;i++) scanf("%d",&d[i]);
		scanf("%d",&s);
		ans=0; dfs(1,0,s);
		printf("%lld\n",ans);
	}
	return 0;
}

 

posted @ 2018-04-11 16:11  qjs12  阅读(98)  评论(0编辑  收藏  举报