题解:洛谷 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
【核心思想】
-
问题分析:给定 \(N\) 道判断题,答案为 true 的题目数量一定是 \(t_1, t_2, \dots, t_K\) 中的某一个。约翰需要制定答题策略(确定每道题答 true 或 false),使得在最坏情况下(即 true 数量取使得分最低的那个 \(t_i\))的得分尽可能高。这是一个博弈论/最优化问题,核心在于分析三种策略(全 false、全 true、混合策略)的最坏情况。
-
算法选择:
- 分类讨论 + 贪心:分析三种策略的最坏情况,取最大值
- 排序:将可能的 true 数量排序,便于分析相邻区间
-
关键步骤:
- 读入数据:\(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\)
-
时间/空间复杂度:
- 时间复杂度:\(O(K \log K)\),排序 \(O(K \log K)\),遍历 \(O(K)\)
- 空间复杂度:\(O(K)\),存储 \(t\) 数组
-
博弈论最优策略的核心思想:
- 最坏情况最大化:约翰不知道具体哪个 \(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
浙公网安备 33010602011771号