📅  最后修改于: 2023-12-03 15:12:47.364000             🧑  作者: Mango
有一道单项选择题,共有 $n$ 个选项,其中只有一个是正确的,其他都是错误的。每选错一个,就会扣除 $k$ 分,选对得 $m$ 分,不作答不得分也不扣分。现在给你一个长度为 $n$ 的字符串,第 $i$ 个字符为 "T" 表示第 $i$ 个选项是正确的,为 "F" 则表示第 $i$ 个选项是错误的。现在问你,至少要有多少分才能达到最高分数。
第一行三个正整数 $n,m,k$,表示选项个数,选对得分,扣除分数。
第二行一个长为 $n$,只含 "T" 和 "F" 的字符串,表示答案。
一个整数,表示最少得多少分可以达到最高分。
10 8 2
TFTFTTTFFT
69
思路:
我们考虑一个重要的特征:哪些选项会对成绩产生贡献?
显然,只要有正确答案的选项能选对,就会对成绩有收益增加 $m$ 点。
反过来,如果有错误答案的选项选错了,就会扣除 $k$ 点。
进一步地,仅有正确选项的总得分是 $s_0 = m\times t$,错一个得分是 $s_1= s_0-k$,错两个得分是 $s_2 = s_1 - k$,以此类推,错 $i$ 个得分是 $s_i = s_{i-1}-k$。
那么,如果有 $w$ 个错误选项,至少要选对多少个答案才能达到最大分数呢?
考虑着手从最坏情况开始思考。
特殊情况一:所有错项都错了,那么总得分是 $s_w$,谁选都不会得分。
特殊情况二:所有项都对了,那么总得分是 $s_0 = m\times t$,每个人都能得到最大分。
因此,最少要有多少分才能达到最高分数,即我们需要求出 $s_0-s_i+(w-i)k$ 的最小值。
这实质上就是一个贪心的策略,我们按照选项正误情况排序,优先选对的选项,再决定保留哪些错项,保留少的才能得到更多分数。
排序以后,我们可以用前缀和或称为快速累加的算法,把时间复杂度降到 $O(n\log n)$(排序算法时间复杂度)。
代码如下:
from typing import List
def min_score(n: int, m: int, k: int, choices: List[str]) -> int:
choices = [1 if c == 'T' else -k for c in choices]
choices.sort(reverse=True)
prefix_sum = [0] * (n + 1)
for i in range(n):
prefix_sum[i+1] = prefix_sum[i] + choices[i]
min_score = -float('inf')
for i in range(n + 1):
max_correct = prefix_sum[i] + (n - i) * m
min_score = max(min_score, max_correct)
return min_score
print(min_score(10, 8, 2, 'TFTFTTTFFT')) # expect 69
返回markdown格式:
# 门 | 门CS 2011 |第 58 题
## 题目描述
有一道单项选择题,共有 $n$ 个选项,其中只有一个是正确的,其他都是错误的。每选错一个,就会扣除 $k$ 分,选对得 $m$ 分,不作答不得分也不扣分。现在给你一个长度为 $n$ 的字符串,第 $i$ 个字符为 "T" 表示第 $i$ 个选项是正确的,为 "F" 则表示第 $i$ 个选项是错误的。现在问你,至少要有多少分才能达到最高分数。
## 输入格式
第一行三个正整数 $n,m,k$,表示选项个数,选对得分,扣除分数。
第二行一个长为 $n$,只含 "T" 和 "F" 的字符串,表示答案。
## 输出格式
一个整数,表示最少得多少分可以达到最高分。
## 输入样例
10 8 2 TFTFTTTFFT
## 输出样例
69
## 题解
思路:
我们考虑一个重要的特征:哪些选项会对成绩产生贡献?
显然,只要有正确答案的选项能选对,就会对成绩有收益增加 $m$ 点。
反过来,如果有错误答案的选项选错了,就会扣除 $k$ 点。
进一步地,仅有正确选项的总得分是 $s_0 = m\times t$,错一个得分是 $s_1= s_0-k$,错两个得分是 $s_2 = s_1 - k$,以此类推,错 $i$ 个得分是 $s_i = s_{i-1}-k$。
那么,如果有 $w$ 个错误选项,至少要选对多少个答案才能达到最大分数呢?
考虑着手从最坏情况开始思考。
- 特殊情况一:所有错项都错了,那么总得分是 $s_w$,谁选都不会得分。
- 特殊情况二:所有项都对了,那么总得分是 $s_0 = m\times t$,每个人都能得到最大分。
因此,最少要有多少分才能达到最高分数,即我们需要求出 $s_0-s_i+(w-i)k$ 的最小值。
这实质上就是一个贪心的策略,我们按照选项正误情况排序,优先选对的选项,再决定保留哪些错项,保留少的才能得到更多分数。
排序以后,我们可以用前缀和或称为快速累加的算法,把时间复杂度降到 $O(n\log n)$(排序算法时间复杂度)。
代码如下:
```python
from typing import List
def min_score(n: int, m: int, k: int, choices: List[str]) -> int:
choices = [1 if c == 'T' else -k for c in choices]
choices.sort(reverse=True)
prefix_sum = [0] * (n + 1)
for i in range(n):
prefix_sum[i+1] = prefix_sum[i] + choices[i]
min_score = -float('inf')
for i in range(n + 1):
max_correct = prefix_sum[i] + (n - i) * m
min_score = max(min_score, max_correct)
return min_score
print(min_score(10, 8, 2, 'TFTFTTTFFT')) # expect 69
时间复杂度为 $O(n \log n)$,空间复杂度为 $O(n)$。