题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(N\) 头奶牛的陈述,每头奶牛说Bessie的位置 \(\leq p_i\)L型)或 \(\geq p_i\)G型)。求最少撒谎奶牛数,即寻找一个位置 \(x\) 使得满足陈述的奶牛最多(不满足即撒谎)。本质是一个一维最优位置搜索问题。

  2. 算法选择

    • 排序 + 枚举分界点:将所有奶牛按位置 \(p_i\) 排序,枚举每个 \(p_i\) 作为候选位置 \(x\),统计该位置下不满足陈述的奶牛数,取最小值
  3. 关键步骤

    • 读取与排序:读取 \(N\) 头奶牛的类型 \(c_i\)LG)和位置 \(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}\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^2)\),枚举 \(N\) 个位置,每个位置检查 \(N\) 头奶牛
    • 空间复杂度:\(O(N)\),存储奶牛信息
  5. 排序后枚举最优位置的核心思想

    • 最优位置必在陈述点:使最多奶牛满意的躲藏位置一定在某头奶牛的陈述位置 \(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
posted @ 2026-08-29 09:40  团爸讲算法  阅读(10)  评论(0)    收藏  举报