链接:http://vjudge.net/problem/UVA-1606
分析:不妨先假设隔板一定经过至少两个点(否则可以移动隔板使其经过经过两个点,由于在隔板上的点可以看作是在任意一侧,所以总数并不会变小)。最简单的想法是,枚举两个点,然后输出两侧黑白点的个数,枚举量是O(n²),再加上统计的O(n),总时间复杂度是O(n³),太大。
可以先枚举一个基准点,然后将一条直线绕这个基准点旋转。其它点要按照相对基准点的极角排序,这里有一个技巧就是把所有黑点或者白点的坐标作一个中心对称,这样以后就只需统计直线一侧点的总数,排好序后,设边的左端点L和右端点R,初始时L=R=0,每次枚举一个点L,L和基准点的连线作为基准边,将R相对于点基准边转过180°,每当扫过一个点,就动态修改(维护)两侧的点数,直到转到180°,然后更新ans最大值,将下一个点作为左端点L,此时前任左端点被移出扫描区域cnt--,继续不断移动右端点直至180°,期间维护最大值ans就好了。
1 #include <cstdio> 2 #include <cmath> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 1000 + 5; 7 8 struct Point { 9 int x, y; 10 double rad; 11 bool operator < (const Point& rhs) const { 12 return rad < rhs.rad; 13 } 14 } op[maxn], p[maxn]; 15 16 int n, color[maxn]; 17 18 bool Left(Point A, Point B) { 19 return A.x * B.y - A.y * B.x >= 0; 20 } 21 22 int solve() { 23 if (n <= 2) return 2; 24 int ans = 0; 25 for (int i = 0; i < n; i++) { 26 int k = 0; 27 for (int j = 0; j < n; j++) 28 if (j != i) { 29 p[k].x = op[j].x - op[i].x; 30 p[k].y = op[j].y - op[i].y; 31 if (color[j]) { p[k].x = -p[k].x; p[k].y = -p[k].y; } 32 p[k].rad = atan2(p[k].y, p[k].x); 33 k++; 34 } 35 sort(p, p + k); 36 37 int L = 0, R = 0, cnt = 2; 38 while (L < k) { 39 if (L == R) { R = (R + 1) % k; cnt++; } 40 while (L != R && Left(p[L], p[R])) { R = (R + 1) % k; cnt++; } 41 cnt--; 42 L++; 43 ans = max(ans, cnt); 44 } 45 } 46 return ans; 47 } 48 49 int main() { 50 while (scanf("%d", &n) == 1 && n) { 51 for (int i = 0; i < n; i++) 52 scanf("%d%d%d", &op[i].x, &op[i].y, &color[i]); 53 printf("%d\n", solve()); 54 } 55 return 0; 56 }
时间: 2024-10-06 14:06:07