题解:洛谷 P8267 Counting Liars
【题目来源】
洛谷:P8267 [USACO22OPEN] Counting Liars B - 洛谷
【题目描述】
奶牛 Bessie 躲在数轴上的某处。Farmer John 的 \(N\) 头奶牛(\(1\le N\le 1000\))中的每头奶牛都有一条信息要分享:第 \(i\) 头奶牛说 Bessie 躲在小于或等于 \(p_i\) 的某个位置,或者说 Bessie 躲在大于或等于 \(p_i\) 的某个位置(\(0\le p_i\le 10^9\))。
不幸的是,可能不存在躲藏位置与所有奶牛的回答均一致,这意味着并非所有奶牛都在说真话。计算在撒谎的奶牛的最小数量。
【输入】
输入的第一行包含 \(N\)。
以下 \(N\) 行每行包含字符 L 或 G,之后是一个整数 \(p_i\)。L 表示第 \(i\) 头奶牛说 Bessie 的躲藏位置小于或等于 \(p_i\),而 G 表示第 \(i\) 头奶牛说 Bessie 的躲藏位置大于或等于 \(p_i\)。
【输出】
输出在撒谎的奶牛的最小数量。
【输入样例】
2
G 3
L 5
【输出样例】
0
【核心思想】
-
问题分析:给定 \(N\) 头奶牛的陈述,每头奶牛说Bessie的位置 \(\leq p_i\)(
L型)或 \(\geq p_i\)(G型)。求最少撒谎奶牛数,即寻找一个位置 \(x\) 使得满足陈述的奶牛最多(不满足即撒谎)。本质是一个一维最优位置搜索问题。 -
算法选择:
- 排序 + 枚举分界点:将所有奶牛按位置 \(p_i\) 排序,枚举每个 \(p_i\) 作为候选位置 \(x\),统计该位置下不满足陈述的奶牛数,取最小值
-
关键步骤:
- 读取与排序:读取 \(N\) 头奶牛的类型 \(c_i\)(
L或G)和位置 \(p_i\),按位置升序排序 - 枚举候选位置:对于每头奶牛的位置 \(p_i\) 作为候选分界点 \(\text{tmp}\):
- 初始化 \(\text{cnt} = 0\)(撒谎奶牛计数)
- 遍历所有奶牛 \(j\):
G型奶牛要求 \(x \geq p_j\):若 \(\text{tmp} < p_j\),则该奶牛撒谎,\(\text{cnt}++\)L型奶牛要求 \(x \leq p_j\):若 \(\text{tmp} > p_j\),则该奶牛撒谎,\(\text{cnt}++\)
- 更新最小值:\(\text{minn} = \min(\text{minn}, \text{cnt})\)
- 输出答案:\(\text{minn}\)
- 读取与排序:读取 \(N\) 头奶牛的类型 \(c_i\)(
-
时间/空间复杂度:
- 时间复杂度:\(O(N^2)\),枚举 \(N\) 个位置,每个位置检查 \(N\) 头奶牛
- 空间复杂度:\(O(N)\),存储奶牛信息
-
排序后枚举最优位置的核心思想:
- 最优位置必在陈述点:使最多奶牛满意的躲藏位置一定在某头奶牛的陈述位置 \(p_i\) 处(或边界),因为两个相邻 \(p_i\) 之间的任意位置对应的满意奶牛集合相同
- 线性扫描验证:对每个候选位置,分别检查
G型和L型奶牛的满意度,G要求位置在其右侧,L要求位置在其左侧 - 对称性简化:由于只需统计不满足数量,无需实际确定Bessie位置,只需比较候选位置与每头奶牛陈述位置的关系
- 可优化性:可用前缀和将 \(O(N^2)\) 优化到 \(O(N)\),但 \(N \leq 1000\) 时当前复杂度已足够
- 适用于一维最优位置、区间覆盖、满意度最大化类问题
【解题思路】

【算法标签】
普及- #其他排序
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n; // 奶牛的数量
int minn = 1e9; // 存储最小不满意奶牛数,初始化为极大值
// 奶牛结构体,存储奶牛类型和位置
struct node
{
char c; // 奶牛类型:'G'或'L'
int pos; // 奶牛的位置
} p[1005]; // 奶牛数组
/**
* 比较函数,用于按位置排序奶牛
* @param a 第一个奶牛
* @param b 第二个奶牛
* @return 是否a的位置小于b的位置
*/
bool cmp(node a, node b)
{
return a.pos < b.pos;
}
int main()
{
// 输入奶牛数量
cin >> n;
// 输入每头奶牛的类型和位置
for (int i = 1; i <= n; i++)
{
cin >> p[i].c >> p[i].pos;
}
// 按奶牛位置排序
sort(p + 1, p + n + 1, cmp);
// 遍历每头奶牛的位置作为分界点
for (int i = 1; i <= n; i++)
{
int tmp = p[i].pos; // 当前分界点位置
int cnt = 0; // 不满意奶牛计数器
// 检查每头奶牛是否满意当前分界点
for (int j = 1; j <= n; j++)
{
if (p[j].c == 'G')
{
// G型奶牛希望分界点在其右侧
if (tmp < p[j].pos)
{
cnt++;
}
}
else if (p[j].c == 'L')
{
// L型奶牛希望分界点在其左侧
if (tmp > p[j].pos)
{
cnt++;
}
}
}
// 更新最小不满意奶牛数
minn = min(minn, cnt);
}
// 输出最小不满意奶牛数
cout << minn << endl;
return 0;
}
【运行结果】
G 3
L 5
0
浙公网安备 33010602011771号