题目链接
题目大意
- 输入第一行给出一个圆的圆心坐标及半径,再给出n个点,要求在这个圆中取出一个半圆,使这个半圆覆盖点数最多,输出这个最大点数
示例输入
- 第一行:圆的圆心坐标和圆的半径
- 第二行:点的个数 n
- 第二行后面的n行:每个点的坐标
- 输入的测试数据有若干组,输入的测试数据的结束标志,输入一个圆的圆心坐标,半径为负数
25 25 3.5
7
25 28
23 27
27 27
24 23
26 23
24 29
26 29
350 200 2.0
5
350 202
350 199
350 198
348 200
352 200
995 995 10.0
4
1000 1000
999 998
990 992
1000 999
100 100 -2.5
示例输出
3
4
4
解题思路
- 对每组测试数据的n个点,只需保留在圆内的点即可,因为不在圆内的点不可能在半圆内,排除无效的点避免无效判断
- 由于我们不可能枚举半圆所有的角度,因为半圆所有的角度无限多个,即半圆的放法是有无限多种的,因此我们应该考虑哪些半圆的放法是有用的。
- 其实我们只需要枚举半圆的直径和圆内的任一点重合的情况即可,即枚举圆内的任一点在半圆的直径上的所有可能得情况下,半圆的放法,因为最优的半圆一定刚好卡上一个点,这样就才可以留充足的空间给别的点,使得半圆内的点个数最大。
- 所以我们只需暴力枚举边界(圆内的任一点在直径上),然后用叉积统计一遍在此种放置下,半圆内的点的个数,最后从所有可能的情况中取最大值即可
- 在计算时,直径向量采取圆心指向圆内在直径上的点的表示方式,半圆在直径的左边
- 因此,和半径向量的叉积大于等于0(逆时针方向),在半圆内;否则不在半圆内(顺时针方向)
解题代码
#include <iostream>
using namespace std;
int main() {
double circle_x, circle_y, r;
while (cin >> circle_x >> circle_y >> r) {
if (r < 0) break;
int n = 0;
cin >> n;
double xi[100000];
double yi[100000];
int cnt = 0;
for (int i=0; i<n; i++) {
double x, y;
cin >> x >> y;
if ( ((x - circle_x) * (x - circle_x) + (y - circle_y) * (y - circle_y)) <= r * r ) {
xi[cnt] = x;
yi[cnt] = y;
cnt++;
}
}
int ans = 0;
for (int i=0; i<cnt; i++) {
double x = xi[i];
double y = yi[i];
double diameter_x = x - circle_x;
double diameter_y = y - circle_y;
int max_now = 0;
for (int j=0; j<cnt; j++) {
double vector_x = xi[j] - circle_x;
double vector_y = yi[j] - circle_y;
if ((vector_x * diameter_y) - (vector_y * diameter_x) >= 0) {
max_now++;
}
}
max_now = max_now > (cnt - max_now) ? max_now : (cnt - max_now);
ans = ans < max_now ? max_now : ans;
}
cout << ans << endl;
}
}
- 不知道为什么数组大小写n,动态创建数组,在POJ会报编译异常,所以数组大小直接用字面量了