第一次看到这个题,想到的是搜索扫描,枚举直线方程,再对每个点去扫,55–这真的很菜鸟把
看dl博客,知道原来这是一个数学题,兴奋,用到了高数学的数学知识诶,我其实还没有做过关于数学的题呢。
https://morslin.github.io/2019/02/14/uva1606-AmphiphilicCarbonMolecules/#正解
超喜欢上面这个dl的博客风格,真的超喜欢,从它的代码里理了一个解题思路
1.把n个点存起来,把每个点都作为一个坐标原点进行枚举,这也就意味着每个点的坐标会随着坐标原点的变化而变化,利用相对坐标原理和atan2函数,建立极坐标。
2.对每个点建立坐标后,也对新得到的点根据极角大小去排序,这是为了方便下面第3步。
3.想像一下,这时就是一根杆在绕着原点一直转,经过一个点,就确定一个直线情况,得到这个情况下的ans,那么这个直线的枚举情况就是从第一个点枚举到最后一个点。由于已经排好序了,这一半是a个,那么另一半就是n-a个,再分别对这两半去统计得ans。这里博主很巧妙运用了 %
while(R != L && flag(a[L], a[R])) R = (R%tot) + 1, ++ans;
但是自己写的时候,发现博主有个更大的智慧,它进行了一个优化,如果没有那个优化,按我上面想的那样子,那么对一个点建立坐标后,又不断枚举每一个点确定一个直线的状况,那么也就是nnn,但是事实上每一因为直线的改变,不用重新枚举点,实际上是减去原来那个点,再加上新增加的范围内的点。这是第一个优化点。
第2个优化点,是博主把黑色的点的坐标都乘上-1,如此就使得原来要统计2边的黑白相加的最大值,现在相当于把对面的黑变成了这边的白。
而在判断是不是同一边,博主利用的是叉乘,因为
a
⃗
⋅
b
⃗
=
∣
a
∣
∗
∣
b
∣
∗
s
i
n
θ
=
a
x
∗
b
y
−
a
y
∗
b
x
\vec{a} \cdot \vec{b}=|a|*|b|*sin\theta=a_x*b_y-a_y*b_x
a⋅b=∣a∣∗∣b∣∗sinθ=ax∗by−ay∗bx
可以知道,如果 如果叉乘小于0,那么差角就大于180度,那么就是在另一边。
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1e3+10;
struct re{
int x,y,col;};
re all[maxn];
struct point{
double angle;
int col,x,y;
bool operator< (const point &a) const{return angle<=a.angle;} };
int n,ans;
point now[maxn];
int judge(int a,int b){if(a==b)return -1;if(a-b>0)return 1;else return 0;}
void made(int a)
{
int k=0;
for(int i=1;i<=n;i++)
if(i!=a)
{
now[++k].x=all[i].x-all[a].x;
now[k].y=all[i].y-all[a].y;
if(all[i].col)now[k].y*=-1,now[k].x*=-1;
now[k].angle=atan2(now[k].y,now[k].x);
}
sort(now+1,now+n);
int le=1,re=1,tem=1;//tem =1代表的是作为原点的那个点被记入其中。
while(le<k)
{
if(le==re){re=re%k+1;tem++;}
while(le!=re&&judge(now[re].y*now[le].x,now[re].x*now[le].y)){re=re%k+1;tem++;}
ans=max(tem,ans);
le++;tem--;
}
}
int main(void)
{
while(scanf("%d",&n)&&n)
{
ans=0;
for(int i=1;i<=n;i++)
scanf("%d %d %d",&all[i].x,&all[i].y,&all[i].col);
for(int i=1;i<=n;i++)
made(i);
printf("%d\n",ans);
}
return 0;
}