P1450 [HAOI2008]硬币购物 容斥原理+完全背包
题意:
共有 4 种硬币。面值分别为 \(c_1,c_2,c_3,c_4\)。
某人去商店买东西,去了 \(n\) 次,对于每次购买,他带了 \(d_i\) 枚 \(i\) 种硬币,想购买 \(s\)的价值的东西。请问每次有多少种付款方法。
范围&性质:\(1\le n\le 10^3,1\le c,d,s\le 10^5\)
分析:
不考虑个数限制时就是个很裸的完全背包,但是加上了限制那么我们就考虑将不合法的方案全都减掉,对于一个物品\(i\)不合法的方案数可以看做第\(i\)个物品被强制选了\(d[i]+1\)剩下的又可以看做一个没有限制的完全背包,然后对于每一种情况容斥原理组合到一起
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
const int maxn = 1e5+5;
long long f[maxn],c[5],d[5];
long long ans;
int t,s;
void work()
{
scanf("%lld%lld%lld%lld%d",&c[1],&c[2],&c[3],&c[4],&t);
f[0]=1;
for(int i=1;i<=4;i++)
{
for(int j=c[i];j<=100000;j++)
{
f[j]+=f[j-c[i]];
}
}
while(t--)
{
scanf("%lld%lld%lld%lld%d",&d[1],&d[2],&d[3],&d[4],&s);
ans=f[s];
for(int i=1,sum,j,k,tmp;i<=15;i++)
{
sum=s;
for(j=1,k=1,tmp=i;tmp;tmp>>=1,j++)
{
if(tmp&1) k^=1,sum-=(d[j]+1)*c[j];
}
if(sum>=0) k?ans+=f[sum]:ans-=f[sum];
}
printf("%lld\n",ans);
}
}
}
int main()
{
zzc::work();
return 0;
}