代入直线方程检查两类点内部同号且两类代表符号相反。
OJ: shumeng
题目 ID: CSP202006A
难度:入门
标签:模拟几何分类
日期: 2026-07-31 16:21
形式化题目
有
对每条直线判断它是否把两类点完全分到两侧:一侧只有 A 类点,另一侧只有 B 类点。数据保证没有任何点恰好落在直线上。
思路
把每个点
判断步骤
- 记录第一个 A 点与第一个 B 点的符号,作为两类各自的代表符号;
- 扫描全部点:A 点都必须与 A 的代表符号相同,B 点都必须与 B 的代表符号相同;
- 两个代表符号还必须相反,说明两类点分居直线两侧。
三个条件全部满足时输出 Yes,否则输出 No。
先看一个朴素的两遍扫描实现,第一遍确定每类代表符号,第二遍验证所有点:
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-31 16:21
* update_at: 2026-08-17 22:39
*/
// brute.cpp:小数据暴力解,两遍扫描先确定每类代表符号,再验证所有点。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n, query_count;
long long x[MAXN], y[MAXN]; // 点的横坐标、纵坐标
char type[MAXN]; // 点的类别:A 或 B
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> query_count;
for (int i = 1; i <= n; i++) cin >> x[i] >> y[i] >> type[i];
while (query_count--) {
long long theta0, theta1, theta2;
cin >> theta0 >> theta1 >> theta2;
// 第一遍:确定 A、B 两类各自的代表符号
int sign_a = 0, sign_b = 0;
for (int i = 1; i <= n; i++) {
long long value = theta0 + theta1 * x[i] + theta2 * y[i];
int sign = value > 0 ? 1 : -1;
if (type[i] == 'A' && sign_a == 0) sign_a = sign;
if (type[i] == 'B' && sign_b == 0) sign_b = sign;
}
// 第二遍:验证所有点与各自类别代表符号一致,且两类符号相反
bool correct = sign_a != sign_b;
for (int i = 1; i <= n; i++) {
long long value = theta0 + theta1 * x[i] + theta2 * y[i];
int sign = value > 0 ? 1 : -1;
if (type[i] == 'A' && sign != sign_a) correct = false;
if (type[i] == 'B' && sign != sign_b) correct = false;
}
cout << (correct ? "Yes" : "No") << '\n';
}
return 0;
}代码
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-31 16:21
* update_at: 2026-08-17 22:39
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n, query_count;
long long x[MAXN], y[MAXN]; // 点的横坐标、纵坐标
char type[MAXN]; // 点的类别:A 或 B
// 返回 value 的符号:正为 1,负为 -1(题目保证点不落在直线上)
int get_sign(long long value) {
return value > 0 ? 1 : -1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> query_count;
for (int i = 1; i <= n; i++) cin >> x[i] >> y[i] >> type[i];
while (query_count--) {
long long theta0, theta1, theta2;
cin >> theta0 >> theta1 >> theta2;
int sign_a = 0, sign_b = 0; // A 类与 B 类各自的代表符号
bool correct = true;
for (int i = 1; i <= n; i++) {
// 代入直线方程,结果的正负表示点在直线的哪一侧
long long value = theta0 + theta1 * x[i] + theta2 * y[i];
int sign = get_sign(value);
if (type[i] == 'A') {
if (sign_a == 0) sign_a = sign;
else if (sign_a != sign) correct = false;
} else {
if (sign_b == 0) sign_b = sign;
else if (sign_b != sign) correct = false;
}
}
// 两类点必须在直线两侧,即代表符号相反
if (sign_a == sign_b) correct = false;
cout << (correct ? "Yes" : "No") << '\n';
}
return 0;
}复杂度
每条查询扫描全部
总结
线性分类器的“分隔”条件不需要做复杂的几何运算。把所有点代入同一个一次式,检查同类同号、异类异号即可。
