P1450 [HAOI2008]硬币购物

本文探讨了如何利用多重集组合数的理论解决实际问题,将给定的硬币支付方案计数问题转换为完全背包模型,通过巧妙地应用容斥原理和完全背包预处理,将复杂度降低到O(4n+24n),显著提升了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Label

基本多重集组合数问题+完全背包

Description

给定4种面值的硬币,第iii种的面值是CiC_iCin(n≤103)n(n\le 10^3)n(n103)次询问,每次询问给出每种硬币的持有数量DiD_iDi与价格S(S≤105)S(S\le 10^5)S(S105),问用给出硬币付款的方案总数。

solution

若用分组背包做此题,复杂度为O(4nS)O(4nS)O(4nS),实际运行时会超时,故考虑复杂度更低的解法。

借用求多重集组合数的思路,原问题即转化为:给定不定方程∑i=14Cixi=S\sum_{i=1}^4C_ix_i=Si=14Cixi=S444个约束条件xi≤Dix_i\le D_ixiDi,求该不定方程的非负整数解的个数。

采用同样的容斥方式,最后每组询问便转化为求24−12^4-1241次无限制的不定方程∑i=14Cixi=S−∑i=14Cai(Dai+1)\sum_{i=1}^4C_ix_i=S-\sum_{i=1}^{4}C_{a_i}(D_{a_i}+1)i=14Cixi=Si=14Cai(Dai+1)的值。

与原问题不同的是,此时求∣⋂i=1k∁USai∣|\bigcap_{i=1}^{k}\complement_US_{a_i}|i=1kUSai无法利用排列组合直接推通式,但其实求∑i=14Cixi=S−∑i=14Cai(Dai+1)\sum_{i=1}^4C_ix_i=S-\sum_{i=1}^{4}C_{a_i}(D_{a_i}+1)i=14Cixi=Si=14Cai(Dai+1)的解的个数即为用***所有种类***的无限个CiC_iCi***恰好***装满容量为S−∑i=14Cai(Dai+1)S-\sum_{i=1}^{4}C_{a_i}(D_{a_i}+1)Si=14Cai(Dai+1)的背包的方案数。

我们利用完全背包O(4n)O(4n)O(4n)预处理出利用不限制个数的四种硬币凑得值为nnn的方案数f[n]f[n]f[n],对于每一组询问,答案即为:
f[n]+∑k=1n(−1)k∑ai<ai+1∣a∣=kf[S−∑i=1kCai(Dai+1)]f[n]+\sum_{k=1}^{n}(-1)^k\sum_{a_i<a_{i+1}}^{|a|=k}f[S-\sum_{i=1}^kC_{a_i}(D_{a_i}+1)]f[n]+k=1n(1)kai<ai+1a=kf[Si=1kCai(Dai+1)]

对于每组询问只需O(24)O(2^4)O(24)求解即可。算法总复杂度O(4n+24n)O(4n+2^4n)O(4n+24n)

Code

#include<cstdio>
#include<iostream>
#define ri register int
#define ll long long
using namespace std;

const int MAXN=1e5;
int N=4,c[5],T,d[5],S,tot,cnt,use[5];
ll f[MAXN+20],ans;

void dp()
{
	f[0]=1;
	for(ri i=1;i<=4;++i)
		for(ri v=c[i];v<=MAXN;++v) f[v]+=f[v-c[i]];
}

void inex()
{
	for(ri i=1;i<=N;++i) scanf("%d",&d[i]);
	scanf("%d",&S);
	ans=f[S];
	for(ri s=1;s<(1<<N);++s)
	{
		cnt=0,tot=0;
		for(ri i=1;i<=N;++i)
			if(s&(1<<(i-1)))
				++cnt,tot+=(d[i]+1)*c[i];
		if(S-tot<0) continue;
		if(cnt&1) ans-=f[S-tot];
		else ans+=f[S-tot];
	}
	cout<<ans<<'\n';
}

int main()
{
	for(ri i=1;i<=N;++i) scanf("%d",&c[i]);
	dp();
	scanf("%d",&T);
	for(ri i=1;i<=T;++i) inex();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值