题目
源地址:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4481
理解
比赛的时候太紧张,题目都没敢读全= =。 实际上题意还是比较清楚的,给定一个平面,上面有两类点,分别用黑白来表示。现在要求要用一根直线将这个平面分成两半,在直线上面的点全都取走,问,最多能取走多少个点。 具体的方法曾经讲到过,就是扫描线算法:任取一个点为原点,建立极坐标系,其他的点使用极角排序,然后扫描来寻找最大值。 在实现的时候有两个注意点:
- atan2的计算误差不可忽略,极角排序的时候要用叉积的方法进行排序,规避精度问题。
- 叉积方法排序之前,需要做一个投射,将这个平面上的点处理到两个象限中去。
代码
#define MAXN 1000+10
int n,pn,ans,cnt,l,r,sum,num,p;
struct node
{
int x,y,sta;
}Point[MAXN], temp[MAXN];
int detmul(const node a, const node b)
{
return a.x*b.y-b.x*a.y;
}
bool cmp(const node a, const node b)
{
return detmul(a,b)>0;
}
int main(int argc, char const *argv[])
{
while(scanf("%d", &n),n)
{
for(int i=0;i<n;i++)
{
scanf("%d%d%d", &Point[i].x, &Point[i].y,&Point[i].sta);
}
ans=0;
for(pn=0;pn<n;pn++)
{
cnt=0;
for(int i=0;i<n;i++)
{
if(i==pn) continue;
temp[cnt].x=Point[i].x-Point[pn].x;
temp[cnt].y=Point[i].y-Point[pn].y;
temp[cnt].sta=Point[i].sta;
if(temp[cnt].y<0||(temp[cnt].y==0&&temp[cnt].x<0))
{
temp[cnt].x*=-1;
temp[cnt].y*=-1;
temp[cnt].sta=!temp[cnt].sta;
}
cnt++;
}
sort(temp,temp+cnt,cmp);
l=r=sum=0;
for(int i=0;i<cnt;i++)
{
if(temp[i].sta==0) l++;
}
for(int i=0;i<cnt;i=p)
{
num=0;
for(p=i;p<cnt;p++)
{
if(detmul(temp[i],temp[p])) break;
if(temp[p].sta) r++;
else num++;
}
sum=max(sum,l+r+1);
sum=max(sum,cnt-l-r+p-i+1);
l-=num;
}
ans=max(ans,sum);
}
printf("%d\n", ans);
}
return 0;
}
更新日志
- 2014年11月4日 已AC。