Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/AMS-Regular.js

能量石

题目描述

岩石怪物杜达生活在魔法森林中,他在午餐时收集了 NN 块能量石准备开吃。

由于他的嘴很小,所以一次只能吃一块能量石。

能量石很硬,吃完需要花不少时间。

吃完第 ii 块能量石需要花费的时间为 SiSi 秒。

杜达靠吃能量石来获取能量。

不同的能量石包含的能量可能不同。

此外,能量石会随着时间流逝逐渐失去能量。

ii 块能量石最初包含 EiEi 单位的能量,并且每秒将失去 LiLi 单位的能量。

当杜达开始吃一块能量石时,他就会立即获得该能量石所含的全部能量(无论实际吃完该石头需要多少时间)。

能量石中包含的能量最多降低至 0。

请问杜达通过吃能量石可以获得的最大能量是多少?

输入格式

第一行包含整数 TT,表示共有 TT 组测试数据。

每组数据第一行包含整数 NN,表示能量石的数量。

接下来 NN 行,每行包含三个整数 Si,Ei,LiSi,Ei,Li

输出格式

每组数据输出一个结果,每个结果占一行。

结果表示为 Case #x: y,其中 xx 是组别编号(从 11 开始),yy 是可以获得的最大能量值。

数据范围

1T101T10,
1N1001N100,
1Si1001Si100,
1Ei1051Ei105,
0Li1050Li105

输入样例:

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+1Li+1×Si

交换两块能量石后,获得的总能量石E"i+E"i+1Li×Si+1

假如Li+1×SiLi×Si+1,那么可以说,交换后所能获得的能量更多,因此可得性质1

性质1:如果两相邻能量石i,i+1满足Li+1×SiLi×Si+1,那么这两个能量石可进行交换,且交换后能获得更多能量

那么一个任意两相邻能量石都满足Li+1×SiLi×Si+1性质的队列,必然是最优吃法序列,因为对于任意一个不满足此性质的最优解队列,都可以对其内部进行调整,且调整后获得的能量不会更少。

所以可以先对能量石按照SiLi进行排序,排序后队列即为最优解法

由此,就将二维背包转换为了一个一维背包

之后再按照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);
    }
}
posted @   INnoVation-V2  阅读(292)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
历史上的今天:
2018-04-18 红黑树
点击右上角即可分享
微信分享提示