能量石
题目大意:
共有 N 块魔法石每块魔法石,吃掉的时间是 s,价值是 w,每单位时间内损失的能量是 l
每块魔法石刚开吃的时候就会立刻获得他的价值(不会在吃的过程中损失能量)
每块魔法石一旦开吃就不能停下,魔法石能量损失到 0 以后不会额外损失
求一种方案,使得最终获得的 总价值 最大
题目分析:
如果本题抛开能量损失的条件,其实就是一个01背包问题,我们就可以任意次序取能量石都不会影响最终答案。
但现在有了这个限制我们就要考虑按什么顺序取能量石可以最大化获取的能量;
这是就需要用到贪心的思想,
现在我们考虑取 第i 和 第j 个能量石:
1.如果我们先取 第i 个再取 第j 个 获取的能 ei + ej - si * lj
2.如果我们先取 第j 个再取 第i 个 获取的能 ej + ei - sj * li
我们可以发现 当si * lj < sj * li 第一种取法可以获得更多的能量
现在对于任意的最优解如果选择次序中存在 si * lj > sj * li 这时我们总可以交换这取两项次序,并且交换后获取的能量不会减少。
于是得以证明出 贪心解 ≥ 最优解
所以我们只需要按照上述次序 ( s/l 的值) 从小到大排序, 然后直接套01背包模板
f(i,j) 状态表示:考虑前 i 个魔法石,且吃掉最后一个魔法石后,所用总时间恰好为 j 的方案
状态转移:
吃掉第 i 个魔法石 — f(i−1,j−si)+ei−(j−si)×li
不吃第 i 个魔法石 — f(i−1,j)
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int f[N];
struct node
{
int s,e,l;
}stone[N];
bool cmp(const node w1,const node w2)
{
return w1.s * w2.l < w1.l * w2.s;//l有可能为0所以用乘法
}
int main()
{
int t;
cin>>t;
for(int k = 1; k <= t; k++)
{
int n,m=0;
cin>>n;
for(int i = 1; i <= n; i++)
{
int s,e,l;
scanf("%d %d %d", &s, &e,&l);
stone[i] = {s, e, l};
m += s;
}
sort(stone + 1, stone + 1 + n, cmp);
memset(f,-0x3f,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 = 1; i <= m; i++) res=max(res,f[i]);
printf("Case #%d: %d\n",k,res);
}
return 0;
}