链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4481
题意:
平面上有n(n≤1000)个点,每个点为白点或者黑点。现在需放置一条隔板,
使得隔板一侧的白点数加上另一侧的黑点数总数最大。隔板上的点可以看作是在任意一侧。
分析:
扫描法
不妨假设隔板一定经过至少两个点(否则可以移动隔板使其经过两个点,并且总数不会变小),
可以先枚举一个基准点,然后将一条直线绕这个点旋转。每当直线扫过一个点,就可以动态修改两侧的点数。
注意本题存在多点共线的情况,如果用反三角函数计算极角,然后判断极角是否相同的话,很容易产生精度误差。
应该把极角相等的条件进行化简,只使用整数运算进行判断。
代码:
1 #include <cstdio> 2 #include <cmath> 3 #include <algorithm> 4 using namespace std; 5 6 const int UP = 1000 + 5; 7 8 struct DOT { 9 int x, y; 10 double rad; 11 bool operator < (const DOT& that) const { 12 return rad < that.rad; 13 } 14 } dot[UP]; 15 16 int n, X[UP], Y[UP], C[UP]; 17 18 inline bool left(DOT& a, DOT& b){ //判断点a是否在参考原点b的左侧 19 return a.y * b.x >= a.x * b.y; 20 } 21 22 int solve(){ 23 if(n < 4) return n; 24 int ans = 0; 25 for(int i = 0; i < n; i++){ //枚举原点 26 int k = 0; 27 for(int t = 0; t < n; t++){ 28 if(t == i) continue; 29 dot[k].x = X[t] - X[i]; //将点i视为原点,对其他点的坐标进行转换 30 dot[k].y = Y[t] - Y[i]; 31 if(C[t]){ //如果点t是黑点,那么将其变为相对于原点对称的白点,这样结果不变 32 dot[k].x = -dot[k].x; 33 dot[k].y = -dot[k].y; 34 } 35 dot[k].rad = atan2(dot[k].y, dot[k].x); //求出O-dot[k]与正x轴的夹角的弧度值 36 k++; 37 } 38 sort(dot, dot + k); 39 int R = 0, sum = 2; //一开始分隔线上有两个点 40 for(int L = 0; L < k; L++){ //枚举分隔线的另一个点,即以O-dot[L]为分隔线 41 if(R == L){ //为了不与下面的 R != L 发生冲突,将R点左移一位 42 R = (R + 1) % k; 43 sum++; //一开始也可抵消下面的 sum-- 的效果 44 } 45 while(R != L && left(dot[R], dot[L])){ //维护在分隔线左侧的点的个数 46 R = (R + 1) % k; 47 sum++; 48 } 49 sum--; //因为上一次分隔线的左移,上一个dot[L]变成了现在分隔线右侧的点,故总点数减1 50 ans = max(ans, sum); 51 } 52 } 53 return ans; 54 } 55 56 int main(){ 57 while(scanf("%d", &n) && n){ 58 for(int i = 0; i < n; i++) scanf("%d%d%d", &X[i], &Y[i], &C[i]); 59 printf("%d ", solve()); 60 } 61 return 0; 62 }