P1450 [HAOI2008]硬幣購物 - 容斥 - DP
阿新 • • 發佈:2018-11-02
直接做多重揹包複雜度太高了,我們想想是什麼因素限制瞭如此高的複雜度
如果沒有硬幣個數的限制呢?直接用完全揹包預處理後查詢就好了
那麼現在考慮一個較為簡單的問題,只有一種硬幣有限制
設dp[s]為買了價值為s時的方案數,先暫時當做完全揹包預處理出dp陣列
若那個唯一被限制硬幣的面值為c,個數為d,那麼真正的方案數(其他硬幣都是完全揹包)是dp[s] - dp[s - c * (d + 1)]
這個式子有點不顯然,因為這個式子很巧妙。考慮多少方案是多餘的,是拿了d個硬幣以上的方案吧,那麼至少也得拿d + 1個硬幣的情況下,你會發現dp[s - c * (d + 1)]中所有的方案,都可以通過加上d + 1個硬幣c,拼接出
然後就可以容斥搞一下,我們算“多餘的方案數”,設其值為x,先把只考慮某一種硬幣限制的多餘方案數都加到x裡面,然後這樣肯定會加重複的,減去既不滿足1的又不滿足2的方案數,然後再加回來同時不滿足123的方案數,然後用總方案數減去x就好了
如果容斥式子不好寫可以兩個方向各思考一次:既滿足1要求又滿足2要求 或 既不滿足1又不滿足2的,這道題用不滿足比較好寫,因為知道了目前多餘的方案數有多少
既不滿足1要求又不滿足2要求的:dp[s - c1 * (d1 + 1) - c2 * (d2 + 1)]
這個顯然,不滿足的比滿足的方案好想,兩個限制同時不滿足只能是上式
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <cmath> using namespace std; #define debug(x) cerr << #x << "=" << x << endl; const int MAXN = 100000 + 10; const int INF = 1 << 30; typedef long long ll; int c[5],d[5],s,tot; ll f[MAXN]; void init() { f[0] = 1; for(int i=1; i<=4; i++) { for(int j=0; j<=100000; j++) { if(j >= c[i]) f[j] += f[j - c[i]]; } } } int main() { cin >> c[1] >> c[2] >> c[3] >> c[4] >> tot; init(); for(int i=1; i<=tot; i++) { cin >> d[1] >> d[2] >> d[3] >> d[4] >> s; ll ans = f[s]; for(int i=1; i<(1<<4); i++) { int now = s; int x = i, tot = 0; for(int j=1; j<=4; j++) if(x & (1 << (j - 1))) now -= c[j] * (d[j] + 1), tot++; if(now < 0) continue; if(tot % 2) ans -= f[now]; else ans += f[now]; } printf("%lld\n", ans); } return 0; }