●BZOJ 1042 [HAOI2008]硬币购物
题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=1042
题解:
容斥原理,dp预处理
首先跑个无限物品的背包dp求出dp[i]表示在四种物品都有无限个情况下有多少种方法支付 i元。
然后对于每个询问,答案就是 dp[S]-不合法的方法。
那么这个不合法的方法数怎么求呢?
举个例子:如果 c1不能超过d1个的话,那么我们就强制用掉 d1+1个 c1硬币,
那么dp[S-(d1+1)*c1]就是c1不合法的方法数。
所以这样就可以类似的求出其它硬币的不合法的方法数,以及某几种硬币都不合法的方法数,用于容斥计算。
即 ANS=dp[S] - 一种硬币不合法 + 两种硬币不合法 -三种硬币不合法 +四种硬币不合法。
DFS实现
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 105000 #define ll long long #define filein(x) freopen(#x".in","r",stdin); #define fileout(x) freopen(#x".out","w",stdout); using namespace std; ll dp[MAXN],c[10],d[10]; ll tot,ANS,S; void dfs(int p,int num,ll de){ if(p==5) return; ll nde=de+(d[p]+1)*c[p]; ll val=S-nde<0?0:dp[S-nde]; ANS+=val*(((num+1)&1)?-1:1); dfs(p+1,num+1,nde); dfs(p+1,num,de); } int main() { dp[0]=1; for(int i=1;i<=4;i++) { scanf("%lld",&c[i]); for(int j=c[i];j<=100000;j++) dp[j]+=dp[j-c[i]]; } scanf("%lld",&tot); while(tot--){ for(int i=1;i<=4;i++) scanf("%lld",&d[i]); scanf("%lld",&S); ANS=dp[S]; dfs(1,0,0); printf("%lld\n",ANS); } return 0; }
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas