如果,没有紫书上的翻译的话,我觉得我可能读不懂这道题。=_=||

题意:

平面上有n个点,不是白点就是黑点。现在要放一条直线,使得直线一侧的白点与另一侧的黑点加起来数目最多。直线上的点可以看作位于直线的任意一侧。

分析:

首先假设直线经过两个点,否则可以移动直线使其经过两个点,并且总数不会减少。

所以,我们可以先枚举一个基点,然后以这个点为中心。

围绕基点将其他点按照极角排序,将直线旋转,统计符合要求的点的个数。

小技巧:

如果将所有的黑点以基点为中心做一个中心对称,则符合要求的点的个数就变成了直线一侧的点的个数。

 #include <bits/stdc++.h>

 using namespace std;

 const int maxn =  + ;

 int n, color[maxn];

 struct Point
{
int x, y;
Point(int x=, int y=):x(x), y(y) {}
double rad;
bool operator < (const Point& rhs) const
{ return rad < rhs.rad; }
}op[maxn], p[maxn]; Point operator - (const Point& A, const Point& B)
{ return Point(A.x-B.x, A.y-B.y); } int Cross(const Point& A, const Point& B)
{ return A.x*B.y - A.y*B.x; } int solve()
{
//if(n <= 3) return n;
int ans = ;
for(int i = ; i < n; ++i)
{//枚举基点
int k = ;
for(int j = ; j < n; ++j) if(i != j)
{
p[k] = op[j] - op[i];
if(color[j]) { p[k].x = -p[k].x; p[k].y = -p[k].y; }//将黑点做个中心对称
p[k].rad = atan2(p[k].y, p[k].x);
k++;
}
sort(p, p+k); int L = , R = , cnt = ;
for(; L < k; ++L)
{//统计p[L]到p[R]之间的点
if(L == R) { R = (R+)%k; cnt++; }
while(L != R && Cross(p[L], p[R]) >= ) { R = (R+)%k; cnt++; }//当区间大于180度停止
cnt--;
ans = max(ans, cnt);
}
}
return ans;
} int main()
{
freopen("in.txt", "r", stdin); while(scanf("%d", &n) == && n)
{
for(int i = ; i < n; ++i) scanf("%d%d%d", &op[i].x, &op[i].y, &color[i]);
printf("%d\n", solve());
} return ;
}

代码君

04-25 10:30