通过率 0% · 提交 0 · 通过 0
给定 n 个样本的模型分数和真实标签。选择一个阈值 theta,当 score >= theta 时预测为正类,否则预测为负类。阈值只能取某个样本分数。记 TP、FP、FN 分别为真阳性、假阳性、假阴性,本题使用 F1 = 2 * TP / (2 * TP + FP + FN);如果分母为 0,则 F1 定义为 0。请找出 F1 值最大的阈值;如果多个阈值的 F1 相同,选择数值更大的阈值。
这类题属于算法机考高频题型中「华为 AI 岗 / 排序」方向的高频题型,通常考察对「华为 AI 岗 / 排序」的建模能力与边界条件处理。掌握本题的解题思路后,可举一反三应对同类真题方向,稳步提升机考通过率。
第一行输入整数 n。接下来 n 行,每行输入一个实数 score 和一个整数 label,label 只可能为 0 或 1。
输出一行,包含最优阈值和对应 F1。阈值保留四位小数,F1 保留六位小数。
示例 1
输入示例
4 0.9 1 0.8 0 0.7 1 0.4 0
输出示例
0.7000 0.800000
基础阈值选择
示例 2
输入示例
4 0.8 1 0.8 0 0.5 1 0.2 0
输出示例
0.5000 0.800000
相同分数必须一起跨过阈值
时间限制 3000 ms · 内存限制 256 MB
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。
这些是真正决定能不能 AC、但通用题解里常被略过的点。
给定分数和标签,选一个阈值让 F1 最大。考排序扫描的工程化写法:并列分数怎么跨线、F1 并列怎么裁、指标怎么增量维护。
阈值只能取样本分数,候选最多 n 个。暴力对每个候选重算 TP/FP/FN 是 O(n²),n = 50000 会到 25 亿次运算,超时。正解是按分数从高到低排序后一次线性扫:
阈值从最高分开始下移。阈值等于某个分数 s 时,所有分数 ≥ s 的样本预测为正。从高到低逐个「跨线」——把样本从预测负类挪进预测正类:标签为 1 则 TP+1,标签为 0 则 FP+1。FN 恒等于(正样本总数 − TP),三个量都是 O(1) 增量维护。
同分必须一起跨线:分数相同的样本要么都在阈值上方要么都在下方,不存在只挪一半的状态。实现时按相同分数分组,整组更新完计数后才评估这一档的 F1。逐个评估会在重复分数用例上得到一个物理上不存在的阈值状态。
F1 = 2·TP / (2·TP + FP + FN),分母为 0 时 F1 定义为 0。这个式子和 2PR/(P+R) 是同一个数,用哪个算都行,题面口径是前者。
F1 并列取更大阈值:从高到低扫时大阈值先出现,所以更新条件写成「严格大于才更新」,并列时自然保留先出现的大阈值。写成 >= 就会取到最小的并列阈值,方向恰好反掉。
阈值保留四位小数,F1 保留六位小数。数据保证最优阈值不落在舍入中点,常规格式化即可。
排序 O(n log n),扫描 O(n)。
>=,取到了更小的阈值。样例 1 四个样本,按分数从高到低:0.9(正)、0.8(负)、0.7(正)、0.4(负),正样本总数 2。逐档跨线:
| 阈值 | TP | FP | FN | F1 |
|---|---|---|---|---|
| 0.9 | 1 | 0 | 1 | 0.6667 |
| 0.8 | 1 | 1 | 1 | 0.5 |
| 0.7 | 2 | 1 | 0 | 0.8 |
| 0.4 | 2 | 2 | 0 | 0.6667 |
最优是 θ=0.7、F1=0.8,输出 0.7000 0.800000。表里 F1 随阈值不单调——先降后升再降,所以只能全扫不能提前停。
阈值扫描是「precision/recall 权衡」的代码化身,面试标准句:阈值下移,召回只升不降、精确率通常下降,F1 在两者之间找平衡点。追问「为什么 F1 用调和平均」:调和平均对短板敏感,P 和 R 里只要有一个接近 0,F1 就接近 0——算术平均会被另一个高值抬起来,掩盖短板。再深一层的追问是 AUC 与 F1 的区别:AUC 评的是排序质量、与阈值无关,F1 评的是选定阈值后的落地效果;上线要定阈值,所以业务里最终看的往往是这条扫描曲线。
先构造两个同分样本的小用例,验证是否整组跨线;再构造两档 F1 相同的用例,看输出的是不是更大的阈值(更新条件必须是严格大于);TLE 则基本是 O(n²) 重算指标——检查 TP/FP 是否增量维护、FN 是否用「正样本总数 − TP」推出。
# 按分数从高到低排序,同分整组一起跨线,增量维护 TP/FP(FN=正样本总数−TP);
# 每档算 F1=2TP/(2TP+FP+FN),严格大于才更新——并列自然保留更大阈值。
import sys
def solve() -> None:
data = sys.stdin.buffer.read().split()
if not data:
return
n = int(data[0])
items = []
at = 1
for _ in range(n):
score = float(data[at]); at += 1
label = int(data[at]); at += 1
items.append((score, label))
items.sort(reverse=True)
total_pos = sum(label for _, label in items)
tp = fp = 0
best_f1 = -1.0
best_threshold = items[0][0]
i = 0
while i < n:
score = items[i][0]
while i < n and items[i][0] == score:
if items[i][1] == 1:
tp += 1
else:
fp += 1
i += 1
fn = total_pos - tp
denom = 2 * tp + fp + fn
f1 = 0.0 if denom == 0 else (2 * tp) / denom
if f1 > best_f1 + 1e-12 or (abs(f1 - best_f1) <= 1e-12 and score > best_threshold):
best_f1 = f1
best_threshold = score
print(f"{best_threshold:.4f} {best_f1:.6f}")
if __name__ == "__main__":
solve()
登录后可查看你在本题的历史提交,以及每次的各用例通过情况。
© 2026 广州慕课网络科技有限公司 · 吴师兄学算法官网 版权所有