枚举每个陈述中的坐标作为藏身点候选,逐条统计与该位置不一致的陈述数。
OJ: usaco
题目 ID: 1228
难度:入门
标签:枚举数学usaco
日期: 2026-07-11 17:27
题意
Bessie 躲在数轴上的某个整数位置 h。
每头牛给出一条陈述:
G p:它说; L p:它说。
要求选择一个位置 h,让撒谎的牛最少,输出最少数量。
思路
先看一个直接检查候选位置的暴力:
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-11 17:27
* update_at: 2026-07-11 17:28
*/
// brute.cpp:小数据暴力解,用来帮助理解题意并辅助对拍。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
const int INF = 1e9;
int n;
char type[MAXN];
long long p[MAXN];
long long candidate[MAXN * 3];
int candidate_cnt;
int count_liars(long long h) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (type[i] == 'G' && h < p[i]) {
cnt++;
}
if (type[i] == 'L' && h > p[i]) {
cnt++;
}
}
return cnt;
}
void add_candidate(long long x) {
candidate_cnt++;
candidate[candidate_cnt] = x;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> type[i] >> p[i];
add_candidate(p[i]);
if (p[i] > 0) {
add_candidate(p[i] - 1);
}
add_candidate(p[i] + 1);
}
int ans = INF;
for (int i = 1; i <= candidate_cnt; i++) {
int liars = count_liars(candidate[i]);
if (liars < ans) {
ans = liars;
}
}
cout << ans << '\n';
return 0;
}对于固定的藏身点 h,一条陈述是否为谎言很好判断:
G p在h < p时是假话;L p在h > p时是假话。
关键问题是:不能枚举整个数轴,因为
官方解析的观察是,只需要枚举输入中出现过的坐标 p_i。
如果某个最优藏身点不等于任何 p_i,把它向左或向右移动到某个最近的 p_i,撒谎数量不会增加。因此一定存在一个最优位置正好等于某个 p_i。
于是做法很直接:
- 枚举
h = p[i]; - 扫描所有陈述,统计与
h不一致的数量; - 对所有候选取最小值。
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-11 17:27
* update_at: 2026-07-11 17:28
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
const int INF = 1e9;
int n;
char type[MAXN];
long long p[MAXN];
int count_liars(long long h) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (type[i] == 'G' && h < p[i]) {
cnt++;
}
if (type[i] == 'L' && h > p[i]) {
cnt++;
}
}
return cnt;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> type[i] >> p[i];
}
int ans = INF;
for (int i = 1; i <= n; i++) {
int liars = count_liars(p[i]);
if (liars < ans) {
ans = liars;
}
}
cout << ans << '\n';
return 0;
}复杂度
枚举 N 个候选位置,每次扫描 N 条陈述。
时间复杂度为
总结
本题的核心是缩小候选藏身点集合。
虽然数轴很大,但陈述真假只会在输入坐标附近发生变化,所以枚举所有 p_i 就足够得到最优答案。