您的位置:首页 > 其它

(计算几何)HDU 6127 Hard challenge

2017-08-16 19:46 309 查看

HDU 6127 Hard challenge

题意

平面坐标系,给出n个点,保证任意两点的连线不过原点.

每一个点都有一个权值,两点之间的线段的权值等于端点权值之积

问如果过原点做一条直线,直线穿过的线段的权值和最大是多少

解决

这道题我们卡住了,想到了要用极角排序来做,也想到了去枚举每一个经过原点和点的直线,但是思路卡在了”如何表示偏差一点点”上面

//直线左侧的点一定能和直线右侧的点连成线段,那么直线两端任意两点权值积的和,就是两个区间权值和的积
//好像有点绕╮(╯▽╰)╭...
/*
p1         /
/   p3            p1(p3+p4)+p2(p3+p4)=(p1+p2)*(p3+p4)
p2      /
/    p4             手动绘图  ∩_∩)O哈哈哈~
/
*/


容易知道,直线两端任意两点权值积的和,就是两个区间权值和的积(代码里面画了图(想不到吧^_^))

首先按照极角对每个点进行排序

初始直线从y轴开始,把x>=0的点都加到右区间,x<0的点都加到左区间

直线逆时针旋转,每旋转到一个点,把这个点从它原来所在的区间里面剔除掉,加到另一个去见里面

保存最大值

#define rep(i,a,b) for(int i=a;i<(b);++i)
struct point
{
int x,y,val;
double angle;
}P[50005];

bool cmp(point p1,point p2)
{
return p1.angle<p2.angle;
}

int main()
{
int n,cases;
long long ans,lsum,rsum;        //lsum和rsum不会爆int但是相乘会爆int
scanf("%d",&cases);
while(cases--)
{
scanf("%d",&n);
rep(i,0,n){
scanf("%d%d%d",&P[i].x,&P[i].y,&P[i].val);
if(P[i].x==0)           //这里对x=0进行特判,除数不能为0
{
if(P[i].y>0) P[i].angle=PI/2.0;
else P[i].angle=-PI/2.0;
}
else P[i].angle=atan(P[i].y*1.0/P[i].x);
}
//rep(i,0,n) cout<<P[i].angle<<endl;
sort(P,P+n,cmp);

ans=1;
lsum=rsum=0;

//初始先以y轴对做表面进行划分
rep(i,0,n){
if(P[i].x>=0) rsum+=P[i].val;   //出现在y轴右侧(包含y轴)的点全部添加到右区间
else lsum+=P[i].val;
}
//直线左侧的点一定能和直线右侧的点连成线段,那么直线两端任意两点权值积的和,就是两个区间权值和的积
//好像有点绕╮(╯▽╰)╭...
/*
p1         /
/   p3            p1(p3+p4)+p2(p3+p4)=(p1+p2)*(p3+p4)
p2      /
/    p4             手动绘图  ∩_∩)O哈哈哈~
/
*/
ans=lsum*rsum;

rep(i,0,n){                         //根据极角排序的顺序,遍历每一个点,然后把这个点从它之前所在的区间里剔除加到另一个区间
if(P[i].x>=0)                   //原来在右区间,从右区间剔除
{
rsum-=P[i].val;
lsum+=P[i].val;
}
else                            //原来在左区间,从左区间剔除
{
rsum+=P[i].val;
lsum-=P[i].val;
}
ans=max(ans,lsum*rsum);
}
cout<<ans<<endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: