找钱 [动态规划, 背包]

找钱



color{grey}{最初想法}

最后一条限制不好处理, 于是关于 小 LL 的 背包信息 搜出来, 售货员 的背包信息 dpdp,
但是忘记 记忆化 了, 丢了 50pts50pts

505ms505 ms AC 代码 downarrow

#include<bits/stdc++.h>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 1000006;
const int mod = 1e9 + 7;

int N;
int X;
int Ans;
int F2[maxn];
int F3[maxn];
int F4[1005][20005];

struct Node{ int a, b, c; } A[maxn];

int DFS(int k, int s){
        if(k == N+1){ if(s < X) return 0; return F2[s - X]; }
        if(~F4[k][s]) return F4[k][s];
        int res = 0;
        for(reg int i = 1; i <= A[k].b; i ++){
                int ns = s + A[k].a*i;
                if(ns - X >= A[k].a) break ;
                res += DFS(k+1, ns); res %= mod;
        }
        res += DFS(k+1, s); res %= mod;
        return F4[k][s] = res;
}

int main(){
        N = read(), X = read();
        for(reg int i = 1; i <= N; i ++) A[i].a = read(), A[i].b = read(), A[i].c = read();
        std::reverse(A+1, A+N+1); F2[0] = 1;
        for(reg int i = 1; i <= N; i ++){
                for(reg int j = 0; j <= X; j ++) F3[j] = F2[j];
                for(reg int k = 1; k <= A[i].c; k <<= 1){
                        for(reg int j = X; j >= k*A[i].a; j --) F2[j] = (F2[j] + F3[j-k*A[i].a]) % mod;
                        A[i].c -= k;
                }
                if(A[i].c > 0) 
                        for(reg int j = X; j >= A[i].c*A[i].a; j --) F2[j] = (F2[j] + F3[j-A[i].c*A[i].a]) % mod;
        }
        memset(F4, -1, sizeof F4);
        printf("%d
", DFS(1, 0));
        return 0;
}

color{red}{正解部分}

F1[x]F_1[x] 表示 小 LL 凑出 xx 的方案数, F2[x]F_2[x] 表示 售货员 凑出 xx 的方案数,

但是题目中对 小 LL 的钱数 有限制, 于是先使用 完全背包 更新一遍 F1[x]F_1[x],

然后 F1[x]F_1[x] 减去 F1[xai×(bi+1)]F_1[x - a_i imes (b_i + 1)] 得到 满足 物品个数限制 .

状态转移时 限制上界X+ai1X+a_i-1倒序循环 满足 纸币面值限制 .


color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 1000006;
const int mod = 1e9 + 7;

int N;
int X;
int Ans;
int a[maxn];
int b[maxn];
int c[maxn];
int F1[maxn];
int F2[maxn];

int main(){
        N = read(), X = read();
        for(reg int i = 1; i <= N; i ++) a[i] = read(), b[i] = read(), c[i] = read();
        F1[0] = F2[0] = 1;
        for(reg int i = N; i >= 1; i --){
                for(reg int j = a[i]; j < X+a[i]; j ++) F1[j] = (F1[j] + F1[j-a[i]]) % mod;
                for(reg int j = X+a[i]-1; j >= a[i]*(b[i]+1); j --) F1[j] = (F1[j] - F1[j-a[i]*(b[i]+1)] + mod) % mod;
                for(reg int j = a[i]; j < X<<1; j ++) F2[j] = (F2[j] + F2[j-a[i]]) % mod;
                for(reg int j = 2*X-1; j >= a[i]*(c[i]+1); j --) F2[j] = (F2[j] - F2[j-a[i]*(c[i]+1)] + mod) % mod;
        }
        for(reg int y = X; y <= 2*X-1; y ++) Ans = (Ans + 1ll*F1[y]*F2[y-X]%mod) % mod;
        printf("%d
", Ans);
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822423.html