题目
有各种不同面值的货币,每种面值的货币有不同的数量,请找出利用这些货币可以凑成的最接近且小于等于给定的数字cash的金额。
思路
多重背包模板题。
1.朴素的多重背包,每个状态转移时枚举用的某种货币的个数,复杂度:
,TLE。
2.二进制优化,背包九讲里面有。
此处应该注意的是,1…n的任意整数用尽量少的几个整数表示成和的形式的最优解问题。
代码
朴素的多重背包
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;
const int maxn = 10 + 5;
const int maxm = 100000 + 100;
int c, n, k[maxn], w[maxn], d[maxm];
int main() {
while (scanf("%d%d", &c, &n) == 2) {
_rep(i, 1, n) scanf("%d%d", &k[i], &w[i]);
memset(d, 0, sizeof(d));
_rep(i, 1, n)
for (int j = c; j >= w[i]; j--)
_rep(kk, 1, k[i])
if (j >= kk * w[i])
d[j] = max(d[j], d[j - kk * w[i]] + kk * w[i]);
int ans = 0;
_rep(i, 1, c) ans = max(ans, d[i]);
printf("%d\n", ans);
}
return 0;
}
二进制优化
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;
const int maxn = 10 + 5;
const int maxm = 100000 + 100;
int c, n, k[maxn], w[maxn], d[maxm];
int main() {
while (scanf("%d%d", &c, &n) == 2) {
_rep(i, 1, n) scanf("%d%d", &k[i], &w[i]);
memset(d, 0, sizeof(d));
_rep(i, 1, n) {
if (k[i] * w[i] >= c) { // 完全背包过程
_rep(j, w[i], c)
d[j] = max(d[j], d[j - w[i]] + w[i]);
}
else { // 二进制分物体后的01背包问题
int l = 1, amount = k[i];
while (l < amount) {
for (int j = c; j >= w[i] * l; j--)
d[j] = max(d[j], d[j - w[i] * l] + w[i] * l);
amount -= l;
l *= 2;
}
for (int j = c; j >= w[i] * amount; j--)
d[j] = max(d[j], d[j - w[i] * amount] + w[i] * amount);
}
}
int ans = 0;
_rep(i, 1, c) ans = max(ans, d[i]);
printf("%d\n", ans);
}
return 0;
}