[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;
}