P1450 [HAOI2008]硬幣購物

題目描述
硬幣購物一共有4種硬幣。面值分別爲c1,c2,c3,c4。某人去商店買東西,去了tot次。每次帶di枚ci硬幣,買si的價值的東西。請問每次有多少種付款方法。ios

di,s<=100000數組

tot<=1000spa

Solution

徹底揹包數組開不下, 大概要運算一天這樣能出答案
假設沒有帶硬幣的限制, 咱們能夠搞個徹底揹包算出 \(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

Code

#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;
    }
相關文章
相關標籤/搜索