洛谷P2851 [USACO06DEC]The Fewest Coins G 题解 多重背包+完全背包

题目链接:https://www.luogu.com.cn/problem/P2851

解题思路:

对FJ做多重背包,对商人做完全背包。

这里比较困惑我的是背包的容量。其大小的思路来自 IcproX大佬的分享

首先说一下,(a) 上界是 (v_{max}^2)

下面给出理由

若知(v_{max}), 则最多有(v_{max})种不同货币

如果选用(v_{max})张时, 则必有两者重复

如第一篇题解所说,设({s_1, s_2, s_3, ....., s_n})表示前缀和。

则必有(s_k - s_j = w* v_{max})

那么$s_k $可以用 (s_j) + (w)(v_{max}) (此时最优)

最坏时为 (v_{max}^2)

示例代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110, maxv = 30030, V = maxv - 1;
int f1[maxv], f2[maxv], n, T, c[maxn], num[maxn], ans = 0x3f3f3f3f;
void zero_one_pack(int c, int v, int f[]) {
    for (int i = V; i >= c; i --)
        f[i] = min(f[i], f[i-c]+v);
}
void complete_pack(int c, int v, int f[]) {
    for (int i = c; i <= V; i ++)
        f[i] = min(f[i], f[i-c]+v);
}
void multi_pack(int c, int v, int num, int f[]) {
    if (c * num >= V) complete_pack(c, v, f);
    else {
        int x = 1;
        while (x <= num) {
            zero_one_pack(c*x, v*x, f);
            num -= x;
            x *= 2;
        }
        if (num) zero_one_pack(c*num, v*num, f);
    }
}
int main() {
    cin >> n >> T;
    for (int i = 0; i < n; i ++) cin >> c[i];
    for (int i = 0; i < n; i ++) cin >> num[i];
    memset(f1+1, 0x3f, sizeof(int)*(maxv-1));
    memset(f2+1, 0x3f, sizeof(int)*(maxv-1));
    for (int i = 0; i < n; i ++) {
        multi_pack(c[i], 1, num[i], f1);
        complete_pack(c[i], 1, f2);
    }
    for (int i = 0; i+T < maxv; i ++) ans = min(ans, f1[i+T] + f2[i]);
    if (ans == 0x3f3f3f3f) puts("-1");
    else cout << ans << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/quanjun/p/13642654.html