【HAOI 2008】硬币购物

版权声明:转载请注明出处。 https://blog.csdn.net/TungstenC/article/details/83140997

题目描述

硬币购物一共有 4 4 种硬币。面值分别为 c 1 , c 2 , c 3 , c 4 c_1,c_2,c_3,c_4 。某人去商店买东西,去了 t o t tot 次。每次带 d i j d_{ij} c i j c_{ij} 硬币,买 s i s_i 的价值的东西。请问每次有多少种付款方法。 d i , s 100000 d_i,s\le 100000 t o t 1000 tot\le 1000

算法分析

并不会多重背包,有空再去学,然而多重背包貌似也不能做。

先考虑没有限制的情况,就是一个完全背包,设已计算出的用前 n n 种硬币购买价值 i i 的物品的方案数为 f [ i ] f[i] ,则仅给第 j j 种硬币加上选取个数满足 [ 0 , d i , j ] [0,d_{i,j}] 限制时的方案数为 f [ i ] f [ i c [ j ] × ( d [ j ] + 1 ) ] f[i]-f[i-c[j]\times(d[j]+1)]

为什么呢?给第 j j 种硬币加上这种限制后使用该种硬币的数量不得超过 d [ j ] d[j] ,考虑补集转换,我们可以先让它先不使用 d [ i ] + 1 d[i]+1 枚该种硬币,最后再使用 d [ j ] + 1 d[j]+1 枚该种金币,即 f [ i c [ j ] × ( d [ j ] + 1 ) ] f[i-c[j]\times(d[j]+1)] ,这样无论之前怎么选都能保证选择的硬币个数大于 d [ i ] d[i] ,最后再用总的方案数减去不满足条件的方案数就是给第 j j 种硬币加上限制的方案数。

注意到,如果我们给多种硬币加上限制,可能会有重复减去的部分,即该方案既满足使用硬币 x x 的数量大于 d [ x ] d[x] 又满足使用硬币 y y 的数量大于 d [ y ] d[y] ,它的方案数等于 f [ i c [ x ] × ( d [ x ] + 1 ) c [ y ] × ( d [ y ] + 1 ) ] f[i-c[x]\times(d[x]+1)-c[y]\times(d[y]+1)] ,将多减的部分加回去就可以了。这实际上使用的是容斥原理,可以用位运算的方法方便的计算出来。

注意要开 64 64 位整数保存结果。

代码实现

#include <cstdio>
typedef long long int ll;
int c[4],d[4];ll f[100005],sub[4];
int main() {
	f[0]=1;
	for(int i=0;i<4;++i) {
		scanf("%d",&c[i]);
		for(int j=c[i];j<=100000;++j) f[j]+=f[j-c[i]];
	}
	int tot;scanf("%d",&tot);
	while(tot--) {
		for(int i=0;i<4;++i) {
			scanf("%d",&d[i]);
			sub[i]=c[i]*(d[i]+1LL);
		}
		int s;scanf("%d",&s);ll ans=0;
		for(int i=0;i<(1<<4);++i) {
			int cnt=0;ll t=0;
			for(int j=0;j<4;++j) if(i>>j&1) {
				++cnt;t+=sub[j];
			}
			if(s-t>=0) (cnt&1)?ans-=f[s-t]:ans+=f[s-t];
		}
		printf("%lld\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/TungstenC/article/details/83140997
今日推荐