混合背包

#include <iostream>

using namespace std;
const int N = 100010, MOD = 1e9 + 7;

int n1, n2, m; // n1种普通币,n2种纪念币,凑成面值m
int f[N];

// 背包问题:完全背包(普通币) + 0/1背包(纪念币)
// 时间复杂度: O(n*m)
// f[i][j]:1.只从前i种硬币中选,且总面值是j的选法的集合 2.数量
// 集合可以分为两种:不选硬币i + 选硬币i
// 不选硬币i: f[i][j] = f[i-1][j]
// 选硬币i后:1. 纪念币(0/1背包)f[i][j] = f[i-1][j] + f[i-1][j-p[i]]
// 选硬币i后:2. 普通币种(完全背包)f[i][j] = f[i-1][j] + f[i-1][j-p[i]] + f[i-1][j-2*p[i]] + ...
// 优化:f[i][j-p[i]] = f[i-1][j-p[i]] + f[i-1][j-2*p[i]] + f[i-1][j-3*p[i]] + ...
// f[i][j] = f[i-1][j] + f[i][j-p[i]]
// 二维优化成一维

int main() {
    cin >> n1 >> n2 >> m;

    f[0] = 1; // 一枚硬币都没有的时候,只有一种就是都不选
    for (int i = 1; i <= n1; i ++ ) { // 普通币:完全背包
        int p; // 第i种纪念币的面值是p
        cin >> p;
        for (int j = p; j <= m; j ++ ) f[j] = (f[j] + f[j - p]) % MOD;
    }
    for (int i = 1; i <= n2; i ++ ) { // 纪念币:0/1背包
        int p;
        cin >>  p;
        for (int j = m; j >= p; j -- ) f[j] = (f[j] + f[j - p]) % MOD;
    }
    cout << f[m] << endl;

    return 0;
}
posted @ 2022-05-17 21:34  兮何其  阅读(28)  评论(0)    收藏  举报