1. 程式人生 > 其它 >能量石

能量石

題目描述

岩石怪物杜達生活在魔法森林中,他在午餐時收集了 \(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);
    }
}