【AcWing题解/Google Kickstart2019 Round B Problem B】能量石

原题链接:https://www.acwing.com/problem/content/description/736/
难度:困难(?)
涉及知识点:01背包、贪心算法

题意分析

给定一些能量石,对于第 i i i 块能量石,其初始能量值为 E i E_i Ei,每秒会减少 L i L_i Li 单位的能量,吃完这块能量石需要 S i S_i Si 秒并且会立刻得到这块能量石的能量。能量石的的能量最多降到 0,不会为负数。求杜达最后能获得多少能量。

分析与解决

这一题乍一看确实是没有思路的,但是我们细想一下,在基于闫氏DP思考法即在集合角度思考DP 问题时,往往是考虑了整个集合,那么存不存在一个子集,让我们的注意力都集中在某一个子集中呢?当然有,这个子集就是最优解子集
考虑在最优解子集 a a a 中, 元素的排列为
a 1 , a 2 , a 3 , a 4 , a 5 , . . . , a n − 2 , a n − 1 , a n a_1,a_2,a_3,a_4,a_5,...,a_{n-2},a_{n-1},a_n a1,a2,a3,a4,a5,...,an2,an1,an
对于一组相邻的元素 a i a_i ai a j a_j aj,如果我先吃 a i a_i ai 再吃 a j a_j aj,那么所获取的能量为 E i + E j − S i × L j E_i+E_j-S_i\times L_j Ei+EjSi×Lj;反之所获得的能量为 E j + E i − S j × L i E_j+E_i-S_j\times L_i Ej+EiSj×Li,由于这是最优解子集,我们必然可以得出如下不等式
E i + E j − S i × L j ≥ E j + E i − S j × L i ① E_i+E_j-S_i\times L_j\geq E_j+E_i-S_j\times L_i① Ei+EjSi×LjEj+EiSj×Li
整理得
S i × L j ≤ S j × L i ② S_i\times L_j\leq S_j\times L_i ② Si×LjSj×Li
逆向推理,也就是如果一个集合能够满足这两个不等式,就一定是最优解子集,那么我们就可以在输入数据出通过 s o r t sort sort 排序构造出最优解子集。然后在最优解子集里做 01 背包即可。

AC代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e4 + 10;

struct Stone
{
    int s, e, l;
    bool operator< (const Stone &W) const
    {
        return s * W.l < l * W.s;
    }
}stone[N];
int T, n;
int f[N];

int main()
{
    cin >> T;
    
    for (int C = 1; C <= T; C++)
    {
        int m = 0;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            int s, e, l;
            cin >> s >> e >> l;
            stone[i] = {s, e, l};
            m += s;
        }
        
        sort(stone + 1, stone + n + 1);
        
        memset(f, -0x3f3f3f3f, sizeof f);
        f[0] = 0;
        
        for (int i = 1; i <= n; i++)
        {
            int s = stone[i].s, e = stone[i].e, l = stone[i].l;
            for (int j = m; j >= s; j--)
            {
                f[j] = max(f[j], f[j - s] + e - (j - s) * l);
            }
        }
        
        int res = 0;
        for (int i = 0; i <= m; i++) res = max(res, f[i]);
        printf("Case #%d: %d\n", C, res);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oier_Asad.Chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值