📜  门|门CS 2011 |第 58 题(1)

📅  最后修改于: 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)$。