题解:洛谷 P2988 Test Taking

【题目来源】

洛谷:P2988 [USACO10MAR] Test Taking S

【题目描述】

农夫约翰需要参加一年一度的农民资格考试。考试很简单,只有 \(N\) 道判断题(\(1 \le N \le 10^6\))。然而约翰去年考试却“杯具”了,贝茜希望今年能帮帮他。

贝茜得到可靠的小道消息,答案为 true 的题目数量一定是 \(t_1, t_2, t_3, \dots, t_K\) 中的一个(\(0 \le t_i \le N\)\(0 \le K \le 10^4\))。但具体是哪几道题的答案为 true,她完全不知道。

约翰想要知道,在认真研究了这些内部消息后(虽然不能确定任何一道题的具体答案),他保证能获得的最高的分数是多少?

为了说明贝茜的想法,考虑 \(N = 6\) 的一次考试,贝茜知道答案为 true 的题的数量可能是 0 或者 3

约翰可以这样制定策略:

  • 如果约翰全部答 false
    • 当实际有 0 道题的正确答案是 true 时,约翰答对 6 题;
    • 当实际有 3 道题的正确答案是 true 时,约翰答对 3 题。

因此,只要约翰全部答 false,那么至少一定能答对 3 题,尽管他不知道每道题的确切答案。

【输入】

  • 第一行:两个整数 \(N\)\(K\)
  • 第二行到第 \(K+1\) 行:每行一个整数 \(t_i\)

【输出】

  • 一行:一个整数,表示约翰采用最优策略时,保证能获得的最高分数

【输入样例】

6 2
0
3

【输出样例】

3

【核心思想】

  1. 问题分析:给定 \(N\) 道判断题,答案为 true 的题目数量一定是 \(t_1, t_2, \dots, t_K\) 中的某一个。约翰需要制定答题策略(确定每道题答 true 或 false),使得在最坏情况下(即 true 数量取使得分最低的那个 \(t_i\))的得分尽可能高。这是一个博弈论/最优化问题,核心在于分析三种策略(全 false、全 true、混合策略)的最坏情况。

  2. 算法选择

    • 分类讨论 + 贪心:分析三种策略的最坏情况,取最大值
    • 排序:将可能的 true 数量排序,便于分析相邻区间
  3. 关键步骤

    • 读入数据\(N\)(题目数)、\(K\)(可能的 true 数量个数)、\(t[1..K]\)
    • 排序\(sort(t+1, t+K+1)\)
    • 策略一(全答 false)
      • 最坏情况:实际 true 数量最多(\(t_K\)),答对 \(N - t_K\)
      • \(ans = \max(ans, N - t_K)\)
    • 策略二(全答 true)
      • 最坏情况:实际 true 数量最少(\(t_1\)),答对 \(t_1\)
      • \(ans = \max(ans, t_1)\)
    • 策略三(混合策略)
      • 设约翰答 \(x\) 道 true,\(N-x\) 道 false
      • 实际 true 数量为 \(t\) 时,答对 \(\min(x, t) + \min(N-x, N-t) = \min(x,t) + N - \max(x,t)\)
      • 最优 \(x\) 在相邻 \(t_i, t_{i+1}\) 之间,最坏情况答对 \(\lfloor (t_{i+1} - t_i) / 2 \rfloor\)
      • 遍历 \(i\)\(1\)\(K-1\)\(ans = \max(ans, \lfloor (t_{i+1} - t_i) / 2 \rfloor)\)
    • 输出答案 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(K \log K)\),排序 \(O(K \log K)\),遍历 \(O(K)\)
    • 空间复杂度:\(O(K)\),存储 \(t\) 数组
  5. 博弈论最优策略的核心思想

    • 最坏情况最大化:约翰不知道具体哪个 \(t_i\) 是正确的,必须保证在所有可能情况下的最低得分最高(maximin 策略)
    • 混合策略分析:当 \(x \in [t_i, t_{i+1}]\) 时,最坏情况出现在 \(t = t_i\)\(t = t_{i+1}\),此时答对题数为 \(N - x + t_i\)\(x + N - t_{i+1}\),取 \(x\) 为中点时两者相等,得 \(\lfloor (t_{i+1} - t_i) / 2 \rfloor\)
    • 极端策略不可忽视:全 true 或全 false 在某些情况下(如 \(t_1 = 0\)\(t_K = N\))可能是最优的
    • 相邻区间独立性:最优 \(x\) 只可能出现在相邻 \(t_i\) 之间或端点,无需考虑非相邻区间
    • 适用于信息不完全博弈、最坏情况优化、区间中点策略类问题

【算法标签】

普及 #贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 10005; // 最大可能的t值数量
int n, k; // n:题目总数, k:可能的true题目数量个数
int a[N], ans; // a[i]:第i个可能的true题目数量, ans:保证能获得的最高分数

int main()
{
    cin >> n >> k; // 读入题目数量和可能的true数量个数
    for (int i=1; i<=k; i++) // 读入所有可能的true题目数量
        cin >> a[i];

    sort(a+1, a+k+1); // 排序,便于后续处理

    // 策略一:全部答false
    // 当实际true数量为a[k](最大值)时,答对n-a[k]题
    // 这是全答false策略下的最坏情况
    ans = max(ans, n - a[k]);

    // 策略二:全部答true
    // 当实际true数量为a[1](最小值)时,答对a[1]题
    // 这是全答true策略下的最坏情况
    ans = max(ans, a[1]);

    // 策略三:答一部分true、一部分false(混合策略)
    // 假设约翰答x道题为true,(n-x)道题为false
    // 对于实际true数量为t的情况,答对题数为:min(x, t) + min(n-x, n-t)
    // 即:min(x, t) + n - max(x, t)
    // 最优的x应该在相邻两个可能的t值之间
    // 对于区间[a[i], a[i+1]],最优策略是取x为中间值
    // 此时最坏情况下的答对题数为(a[i+1]-a[i])/2(向下取整)
    for (int i=1; i<k; i++)
    {
        // 在a[i]和a[i+1]之间取中点作为分界
        // 最坏情况下(实际true数量为a[i]或a[i+1])的答对题数
        int t = (a[i+1] - a[i]) / 2;
        ans = max(ans, t);
    }

    cout << ans << endl; // 输出保证能获得的最高分数
    return 0;
}

【运行结果】

6 2
0
3
3
posted @ 2026-08-11 17:37  团爸讲算法  阅读(16)  评论(0)    收藏  举报