題目描述
岩石怪物杜達生活在魔法森林中,他在午餐時收集了 NN 塊能量石准備開吃。
由於他的嘴很小,所以一次只能吃一塊能量石。
能量石很硬,吃完需要花不少時間。
吃完第 \(i\) 塊能量石需要花費的時間為 \(S_i\) 秒。
杜達靠吃能量石來獲取能量。
不同的能量石包含的能量可能不同。
此外,能量石會隨着時間流逝逐漸失去能量。
第 \(i\) 塊能量石最初包含 \(E_i\) 單位的能量,並且每秒將失去 \(L_i\) 單位的能量。
當杜達開始吃一塊能量石時,他就會立即獲得該能量石所含的全部能量(無論實際吃完該石頭需要多少時間)。
能量石中包含的能量最多降低至 0。
請問杜達通過吃能量石可以獲得的最大能量是多少?
范圍
\(1≤T≤10\)
\(1≤N≤100\)
\(1≤S_i≤100\)
\(1≤E_i≤10^5\)
\(0≤L_i≤10^5\)
題解
本題難點在於如何選擇能量石的順序。
假設我們能量石的最優序列為\(a_1,a_2,...,a_k\)
那么,交換第\(i\)和第\(j\)個可得:
\(E_i + (E_j - L_j \times S_i) \leq E_j + (E_i - L_i \times S_j)\)
化簡得:
\(S_i \times L_j \leq S_j \times L_i\)
這樣我們就得到了排序方式,可知最優解一定按這樣的順序出現。
之后就轉化為01背包問題,設\(f_t\)表示用了\(t\)時間能獲得的最大能量,計算即可.
代碼
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const int S = 10010;
struct node {
int S,L;
int E;
}s[N];
bool cmp(node a,node b) {
return a.S * b.L <= a.L * b.S;
}
int f[S];
int t;
int T,n;
int cas;
int main () {
cin >> T;
while(T --) {
cin >> n;
t = 0;
memset(f,-0x3f,sizeof f);
for(int i = 1;i <= n; ++i) {
cin >> s[i].S >> s[i].E >> s[i].L;
t += s[i].S;
}
sort(s + 1,s + n + 1,cmp);
f[0] = 0;
for(int i = 1;i <= n; ++i) {
for(int j = t;j >= s[i].S; --j) {
f[j] = max(f[j],f[j - s[i].S] + max(0,s[i].E - (j - s[i].S) * s[i].L));
}
}
int res = 0;
for(int i = 1;i <= t; ++i) res = max(res,f[i]);
printf("Case #%d: %d\n",++cas,res);
}
return 0;
}