能量石

题目描述

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

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

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

吃完第 \(i\) 块能量石需要花费的时间为 \(S_i\) 秒。

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

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

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

\(i\) 块能量石最初包含 \(E_i\) 单位的能量,并且每秒将失去 \(L_i\) 单位的能量。

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

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

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

范围

\(1≤T≤10\)
\(1≤N≤100\)
\(1≤S_i≤100\)
\(1≤E_i≤10^5\)
\(0≤L_i≤10^5\)

题解

本题难点在于如何选择能量石的顺序。

假设我们能量石的最优序列为\(a_1,a_2,...,a_k\)

那么,交换第\(i\)和第\(j\)个可得:

\(E_i + (E_j - L_j \times S_i) \leq E_j + (E_i - L_i \times S_j)\)

化简得:

\(S_i \times L_j \leq S_j \times L_i\)

这样我们就得到了排序方式,可知最优解一定按这样的顺序出现。

之后就转化为01背包问题,设\(f_t\)表示用了\(t\)时间能获得的最大能量,计算即可.

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const int S = 10010;

struct node {
	int S,L;
	int E;
}s[N];
bool cmp(node a,node b) {
	return a.S * b.L <= a.L * b.S;
}
int f[S];
int t;
int T,n;
int cas;
int main () {
	cin >> T;
	while(T --) {
		cin >> n;
		t = 0;
		memset(f,-0x3f,sizeof f);
		for(int i = 1;i <= n; ++i) {
			cin >> s[i].S >> s[i].E >> s[i].L;
			t += s[i].S;
		}
		sort(s + 1,s + n + 1,cmp);
		f[0] = 0;
		for(int i = 1;i <= n; ++i) {
			for(int j = t;j >= s[i].S; --j) {
				f[j] = max(f[j],f[j - s[i].S] + max(0,s[i].E - (j - s[i].S) * s[i].L));
			}
		}
		int res = 0;
		for(int i = 1;i <= t; ++i) res = max(res,f[i]);
		printf("Case #%d: %d\n",++cas,res);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/akoasm/p/15153800.html