J. 初等元素论
知识点:排序、贪心、枚举、前缀和
先把伤害分为冰火两类,分别从大到小排序。
显然,如果某种元素有 次伤害要打反应,那一定是用伤害最高的
个技能去打。
枚举用火元素触发融化反应的次数 ,反应需要消耗
个火元素技能和
个冰元素技能。假设打完反应还剩
个火元素技能和
个冰元素技能,那么还能进行的冰元素融化反应的次数为
。
排序后使用前缀和可以快速求出前 大的火元素伤害之和,以及前
大的冰元素伤害之和。
关于精度问题:使用64位整数存总伤害的两倍,输出之前除以二并根据奇偶选择小数部分是".0"还是".5"。经过计算发现,直接使用 double 也是可以的,不会有精度丢失。(具体为什么呢?感兴趣的同学可以研究一下 IEEE754 浮点数标准)
标程
C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve()
{
int n;
cin >> n;
vector<int> cryo, pyro;
ll ans1 = 0; // ans1 统计基础伤害(两倍)
for (int i = 0; i < n; ++i)
{
char type;
int damage;
cin >> type >> damage;
if (type == 'C')
cryo.push_back(damage);
else // type == 'P'
pyro.push_back(damage);
ans1 += damage * 2;
}
int cryo_count = cryo.size(), pyro_count = pyro.size();
// 从大到小排序
sort(cryo.rbegin(), cryo.rend());
sort(pyro.rbegin(), pyro.rend());
// 计算 cryo 和 pyro 的前缀和
vector<ll> cryo_sum(cryo_count + 1), pyro_sum(pyro_count + 1);
for (int i = 1; i <= cryo_count; ++i)
cryo_sum[i] = cryo_sum[i - 1] + cryo[i - 1];
for (int i = 1; i <= pyro_count; ++i)
pyro_sum[i] = pyro_sum[i - 1] + pyro[i - 1];
ll ans2 = 0; // ans2 统计因元素反应而增加的伤害(两倍)
const int pyro_melt_max = min(cryo_count, pyro_count); // 火打冰导致的融化反应次数上限
for (int pyro_melt = 0; pyro_melt <= pyro_melt_max; ++pyro_melt) // 枚举火打冰导致的融化反应次数
{
ll pyro_melt_damage = pyro_sum[pyro_melt] * 2; // 火打冰导致的融化反应伤害(两倍)
int cryo_melt = min(cryo_count - pyro_melt, (pyro_count - pyro_melt) * 2); // 冰打火导致的融化反应次数
ll cryo_melt_damage = cryo_sum[cryo_melt]; // 冰打火导致的融化反应伤害(两倍)
ans2 = max(ans2, pyro_melt_damage + cryo_melt_damage);
}
ll ans = ans1 + ans2; // 总伤害(两倍)
cout << (ans / 2) << (ans % 2 == 0 ? ".0" : ".5") << '\n'; // 输出答案(保留一位小数)
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--)
{
solve();
}
}
Java
import java.util.*;
import java.io.*;
public class Main {
static Kattio io = new Kattio();
static void solve() {
int n = io.nextInt();
long ans1 = 0;
int[] cryo = new int[n];
int[] pyro = new int[n];
int cryo_count = 0, pyro_count = 0;
for (int i = 0; i < n; ++i)
{
String type = io.next();
int damage = io.nextInt();
if (type.equals("C"))
{
cryo[cryo_count++] = damage;
}
else
{
pyro[pyro_count++] = damage;
}
ans1 += damage * 2;
}
// 出题人不会写 Java,只会用这种笨办法降序排序。
// 如果有什么高论,欢迎在评论区指出。
cryo = Arrays.copyOf(cryo, cryo_count);
cryo = Arrays.stream(cryo)
.boxed()
.sorted((a, b) -> b - a)
.mapToInt(Integer::intValue)
.toArray();
pyro = Arrays.copyOf(pyro, pyro_count);
pyro = Arrays.stream(pyro)
.boxed()
.sorted((a, b) -> b - a)
.mapToInt(Integer::intValue)
.toArray();
long cryo_sum[] = new long[cryo_count + 1];
long pyro_sum[] = new long[pyro_count + 1];
for (int i = 1; i <= cryo_count; ++i)
{
cryo_sum[i] = cryo_sum[i - 1] + cryo[i - 1];
}
for (int i = 1; i <= pyro_count; ++i)
{
pyro_sum[i] = pyro_sum[i - 1] + pyro[i - 1];
}
long ans2 = 0;
final int pyro_melt_max = Math.min(cryo_count, pyro_count);
for (int pyro_melt = 0; pyro_melt <= pyro_melt_max; ++pyro_melt)
{
long pyro_melt_damage = pyro_sum[pyro_melt] * 2;
int cryo_melt = Math.min(cryo_count - pyro_melt, (pyro_count - pyro_melt) * 2);
long cryo_melt_damage = cryo_sum[cryo_melt];
ans2 = Math.max(ans2, pyro_melt_damage + cryo_melt_damage);
}
long ans = ans1 + ans2;
io.printf("%d%s\n", ans / 2, ans % 2 == 0 ? ".0" : ".5");
}
public static void main(String[] args) {
int T = io.nextInt();
while (T-- > 0) {
solve();
}
io.close();
}
}
class Kattio extends PrintWriter {
private BufferedReader r;
private StringTokenizer st;
// 标准 IO
public Kattio() { this(System.in, System.out); }
public Kattio(InputStream i, OutputStream o) {
super(o);
r = new BufferedReader(new InputStreamReader(i));
}
// 在没有其他输入时返回 null
public String next() {
try {
while (st == null || !st.hasMoreTokens())
st = new StringTokenizer(r.readLine());
return st.nextToken();
} catch (Exception e) {}
return null;
}
public int nextInt() { return Integer.parseInt(next()); }
public double nextDouble() { return Double.parseDouble(next()); }
public long nextLong() { return Long.parseLong(next()); }
}
Python
def solve():
n = int(input())
cryo = []
pyro = []
ans1 = 0
for _ in range(n):
element, damage = input().split()
damage = int(damage)
if element == "C":
cryo.append(damage)
else:
pyro.append(damage)
ans1 += damage * 2
cryo.sort(reverse=True)
pyro.sort(reverse=True)
cryo_sum = [0]
pyro_sum = [0]
for i, damage in enumerate(cryo):
cryo_sum.append(cryo_sum[-1] + damage)
for i, damage in enumerate(pyro):
pyro_sum.append(pyro_sum[-1] + damage)
ans2 = 0
pyro_melt_max = min(len(cryo), len(pyro))
for pyro_melt in range(pyro_melt_max + 1):
pyro_melt_damage = pyro_sum[pyro_melt] * 2
cryo_melt = min(len(cryo) - pyro_melt, (len(pyro) - pyro_melt) * 2)
cryo_melt_damage = cryo_sum[cryo_melt]
ans2 = max(ans2, pyro_melt_damage + cryo_melt_damage)
ans = ans1 + ans2
print("{}.{}".format(ans // 2, "5" if ans % 2 == 1 else "0"))
T = int(input())
for _ in range(T):
solve()