題目描述
硬幣購物一共有4種硬幣。面值分別爲c1,c2,c3,c4。某人去商店買東西,去了tot次。每次帶di枚ci硬幣,買si的價值的東西。請問每次有多少種付款方法。ios
di,s<=100000數組
tot<=1000spa
徹底揹包數組開不下, 大概要運算一天這樣能出答案
假設沒有帶硬幣的限制, 咱們能夠搞個徹底揹包算出 \(maxn\) 內每一個的方案數, 就能夠 \(O(1)\) 回答詢問了
問題是如何解決這個限制問題code
對於第 \(i\) 個硬幣, 咱們只能拿 \(d_{i} * c_{i}\) 這麼多錢
那就是說若是我拿了 \((d_{i} + 1) * c[i]\) 這麼多錢則剩下的不合法
那麼 \(dp[S - (d_{i} + 1) * c[i]]\) 便不合法
而後發現這樣可能會減掉重複的
容斥一下, 減去單數個的加上偶數個的
只有 4 枚硬幣, 能夠狀壓枚舉狀態(0 - 15), 模擬作容斥便可
說不明白的能夠看代碼ci
#include<iostream> #include<cstdio> #include<queue> #include<cstring> #include<algorithm> #include<climits> #define LL long long #define REP(i, x, y) for(LL i = (x);i <= (y);i++) using namespace std; LL RD(){ LL out = 0,flag = 1;char c = getchar(); while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();} while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();} return flag * out; } const LL maxn = 200019; LL c[7], T;//7777777 LL d[7], one[7], S; LL dp[maxn]; void init(){ REP(i, 1, 4)c[i] = RD(), one[i] = 1 << (i - 1); dp[0] = 1; REP(i, 1, 4){ REP(j, c[i], maxn - 7){ dp[j] += dp[j - c[i]]; } } T = RD(); } void solve(){ while(T--){ REP(i, 1, 4)d[i] = RD(); S = RD(); LL ans = 0; REP(i, 0, 15){//每一個狀態 LL temp = S; LL cnt = 0; REP(j, 1, 4){ if(i & one[j]) temp -= (d[j] + 1) * c[j], cnt ^= 1; } if(temp < 0)continue; if(!cnt)ans += dp[temp]; else ans -= dp[temp]; } printf("%lld\n", ans); } } int main(){ init(); solve(); return 0; }