考虑第i个石头和第i+1个石头,如果交换二者吃的顺序。
位置i | 位置i+1 | |
---|---|---|
交换前获得的能量 | Ei | Ei+1 - Si * Li+1 |
交换后获得的能量 | Ei+1 | Ei - Si+1 * Li |
将两位置相加
交换前:Ei + Ei+1 - Si * Li+1
交换后: Ei+1 + Ei - Si+1 * Li
交换后 - 交换前:Si * Li+1 - Si+1 * Li
若想交换后更大,则Si * Li+1 - Si+1 * Li > 0,也就是Si / Li > Si+1 / Li+1
换而言之,就是Si / Li大的放在后面,能让解更优
因此将Si / Li从小到大排序,得到的就是最优的顺序
接下去按照01背包的问题来做,区别是多了减少能量的条件,时间越长物品价值越低(普通的背包体积大小对物品价值并没有影响),因此f[i, j]
的表示要改成 “时间恰好是j
”。
f[i, j]
表示从前i
个能量石中选,时间恰好为j
获得的能量最大值。
不吃第i个能量石:f[i, j] = f[i-1, j]
吃完第i个能量石: f[i, j] = f[i-1, j-s] + e - (j - s) * l
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int t, n, m, f[10005];
struct node {
int s, e, l;
}stone[105];
bool cmp (const node &x, const node &y) {
return x.s * y.l < y.s * x.l;
}
int main() {
scanf("%d", &t);
for (int num = 1; num <= t; num ++) {
scanf("%d", &n);
m = 0;
for (int i = 1; i <= n; i ++) {
scanf("%d%d%d", &stone[i].s, &stone[i].e, &stone[i].l);
m += stone[i].s;
}
sort(stone + 1, stone + n + 1, cmp);
memset(f, 0xc0, sizeof(f));
f[0] = 0;
for (int i = 1; i <= n; i ++) {
for (int j = m; j >= stone[i].s; j--) {
f[j] = max(f[j], f[j - stone[i].s] + stone[i].e - (j - stone[i].s) * stone[i].l);
}
}
int ans = 0;
for (int i = 0; i <= m; i ++) {
ans = max(ans, f[i]);
}
printf("Case #%d: %d\n", num, ans);
}
return 0;
}