能量石
題目描述
岩石怪物杜達生活在魔法森林中,他在午餐時收集了 \(N\) 塊能量石準備開吃。
由於他的嘴很小,所以一次只能吃一塊能量石。
能量石很硬,吃完需要花不少時間。
吃完第 \(i\) 塊能量石需要花費的時間為 SiSi 秒。
杜達靠吃能量石來獲取能量。
不同的能量石包含的能量可能不同。
此外,能量石會隨著時間流逝逐漸失去能量。
第 \(i\) 塊能量石最初包含 \(E_i\) 單位的能量,並且每秒將失去 \(L_i\) 單位的能量。
當杜達開始吃一塊能量石時,他就會立即獲得該能量石所含的全部能量(無論實際吃完該石頭需要多少時間)。
能量石中包含的能量最多降低至 0。
請問杜達通過吃能量石可以獲得的最大能量是多少?
輸入格式
第一行包含整數 \(T\),表示共有 \(T\) 組測試資料。
每組資料第一行包含整數 \(N\),表示能量石的數量。
接下來 \(N\) 行,每行包含三個整數 \(S_i,E_i,L_i\)。
輸出格式
每組資料輸出一個結果,每個結果佔一行。
結果表示為 Case #x: y
,其中 xx 是組別編號(從 11 開始),yy 是可以獲得的最大能量值。
資料範圍
\(1≤T≤10\),
\(1≤N≤100\),
\(1≤Si≤100\),
\(1≤Ei≤105\),
\(0≤Li≤105\)
輸入樣例:
3 4 20 10 1 5 30 5 100 30 1 5 80 60 3 10 4 1000 10 3 1000 10 8 1000 2 12 300 50 5 200 0
輸出樣例:
Case #1: 105
Case #2: 8
Case #3: 500
樣例解釋
在樣例\(#1\)中,有 \(N=4\) 個寶石。杜達可以選擇的一個吃石頭順序是:
- 吃第四塊石頭。這需要 5 秒,並給他 80 單位的能量。
- 吃第二塊石頭。這需要 5 秒,並給他 5 單位的能量(第二塊石頭開始時具有 30 單位能量,5 秒後失去了 25 單位的能量)。
- 吃第三塊石頭。這需要 100 秒,並給他 20 單位的能量(第三塊石頭開始時具有 30 單位能量,10 秒後失去了 10 單位的能量)。
- 吃第一塊石頭。這需要 20 秒,並給他 0 單位的能量(第一塊石頭以 10 單位能量開始,110 秒後已經失去了所有的能量)。
他一共獲得了 105 單位的能量,這是能獲得的最大值,所以答案是 105。
在樣本案例\(#2\)中,有 N=3 個寶石。
無論杜達選擇吃哪塊石頭,剩下的兩個石頭的能量都會耗光。
所以他應該吃第三塊石頭,給他提供 8 單位的能量。
在樣本案例\(#3\)中,有 N=2 個寶石。杜達可以:
- 吃第一塊石頭。這需要 12 秒,並給他 300 單位的能量。
- 吃第二塊石頭。這需要 5 秒,並給他 200 單位的能量(第二塊石頭隨著時間的推移不會失去任何能量!)。
所以答案是 500。
演算法描述
任選兩個相鄰能量石i,i+1,假設在t時刻時,兩能量石能量分別為\(E_i^",E_{i+1}^"\),
那麼先吃i再吃i+1獲得的總能量是\(E_i^"+E_{i+1}^"-L_{i+1}\times S_i\)
交換兩塊能量石後,獲得的總能量石\(E_i^"+E_{i+1}^"-L_i\times S_{i+1}\)
假如\(L_{i+1}\times S_i \ge L_i\times S_{i+1}\),那麼可以說,交換後所能獲得的能量更多,因此可得性質1
性質1:如果兩相鄰能量石\(i,i+1\)滿足\(L_{i+1}\times S_i \ge L_i\times S_{i+1}\),那麼這兩個能量石可進行交換,且交換後能獲得更多能量
那麼一個任意兩相鄰能量石都滿足\(L_{i+1}\times S_i \le L_i\times S_{i+1}\)性質的佇列,必然是最優吃法序列,因為對於任意一個不滿足此性質的最優解佇列,都可以對其內部進行調整,且調整後獲得的能量不會更少。
所以可以先對能量石按照\(\frac{S_i}{L_i}\)進行排序,排序後佇列即為最優解法
由此,就將二維揹包轉換為了一個一維揹包
之後再按照01揹包進行求解即可
程式碼實現
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
struct Stone{
int s, e, l;
bool operator< (const Stone &W) const{
return s * W.l < W.s * l;
}
}stones[10010];
int n;
int dp[10010];
int main(){
int T;
cin >> T;
for(int z = 1; z <= T; z++){
cin >> n;
int m = 0;
for(int x = 0; x < n; x++){
int s, e, l;
cin >> s >> e >> l;
stones[x] = {s, e, l};
m += s;
}
sort(stones, stones + n);
memset(dp, -0x3f, sizeof dp);
dp[0] = 0;
for(int i = 0; i < n; i++){
int s = stones[i].s, e = stones[i].e, l = stones[i].l;
for(int j = m; j >= s; j--){
dp[j] = max(dp[j], dp[j - s] + e - (j - s) * l);
}
}
int res = 0;
for(int i = 0; i <= m; i++) res = max(res, dp[i]);
printf("Case #%d: %d\n", z, res);
}
}