找钱 [动态规划, 背包]

找 钱 找钱



最 初 想 法 \color{grey}{最初想法}

最后一条限制不好处理, 于是关于 小 L L L 的 背包信息 搜出来, 售货员 的背包信息 d p dp dp,
但是忘记 记忆化 了, 丢了 50 p t s 50pts 50pts

505 m s 505 ms 505ms 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\n", DFS(1, 0));
        return 0;
}

正 解 部 分 \color{red}{正解部分}

F 1 [ x ] F_1[x] F1[x] 表示 小 L L L 凑出 x x x 的方案数, F 2 [ x ] F_2[x] F2[x] 表示 售货员 凑出 x x x 的方案数,

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

然后 F 1 [ x ] F_1[x] F1[x] 减去 F 1 [ x − a i × ( b i + 1 ) ] F_1[x - a_i\times (b_i + 1)] F1[xai×(bi+1)] 得到 满足 物品个数限制 .

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


实 现 部 分 \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\n", Ans);
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值