[POI2005]BAN-Bank Notes 题解

[POI2005]BAN-Bank Notes 题解

题目地址:洛谷:【P3423】[POI2005]BAN-Bank Notes – 洛谷、BZOJ:Problem 1531. — [POI2005]Bank notes

注:截止本文发布时,洛谷上的该题仍然0AC,怀疑数据出现问题。

题目描述

Byteotian Bit Bank (BBB) 拥有一套先进的货币系统,这个系统一共有n种面值的硬币,面值分别为b1, b2,…, bn. 但是每种硬币有数量限制,现在我们想要凑出面值k求最少要用多少个硬币.

输入输出格式

输入格式:
第一行一个数 n, 1 <= n <= 200. 接下来一行 n 个整数b1, b2,…, bn, 1 <= b1 < b2 < … < b n <= 20 000, 第三行 n 个整数c1, c2,…, cn, 1 <= ci <= 20 000, 表示每种硬币的个数.最后一行一个数k – 表示要凑的面值数量, 1 <= k <= 20 000.

输出格式:
第一行一个数表示最少需要付的硬币数

输入输出样例

输入样例#1:

3
2 3 5
2 2 1
10

输出样例#1:

3
1 1 1

题解

裸的部分背包,可以做一个求最小值的部分背包即可。对于输出方案,我们可以先二进制拆分成01背包问题,再记录下01背包的转移过程,倒推统计每个物品是否被选中即可。
复杂度O(nk \log n)

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 10005, INF = 1e9;

int n, b[MAXN], c[MAXN], v[MAXN], w[MAXN], bel[MAXN], cnt[MAXN], tot, k, dp[20005];
bool vis[MAXN][20005];

int main() {
    n = readint();
    for(int i = 1; i <= n; i++) {
        b[i] = readint();
    }
    for(int i = 1; i <= n; i++) {
        c[i] = readint();
    }
    k = readint();
    for(int i = 1; i <= n; i++) {
        for(int siz = 1; c[i]; siz <<= 1) {
            siz = std::min(siz, c[i]); c[i] -= siz;
            tot++; w[tot] = siz; v[tot] = b[i] * siz; bel[tot] = i;
        }
    }
    for(int i = 1; i <= k; i++) {
        dp[i] = INF;
    }
    for(int i = 1; i <= tot; i++) {
        for(int j = k; j >= v[i]; j--) {
            if(dp[j] > dp[j - v[i]] + w[i]) {
                dp[j] = dp[j - v[i]] + w[i];
                vis[i][j] = true;
            }
        }
    }
    printf("%d\n", dp[k]);
    for(int i = tot; i >= 1; i--) {
        if(vis[i][k]) {
            cnt[bel[i]] += w[i];
            k -= v[i];
        }
    }
    for(int i = 1; i <= n; i++) {
        printf("%d ", cnt[i]);
    }
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据