(计算几何)HDU 6127 Hard challenge

HDU 6127 Hard challenge

题意

平面坐标系,给出n个点,保证任意两点的连线不过原点.
每一个点都有一个权值,两点之间的线段的权值等于端点权值之积
问如果过原点做一条直线,直线穿过的线段的权值和最大是多少

解决

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


        //直线左侧的点一定能和直线右侧的点连成线段,那么直线两端任意两点权值积的和,就是两个区间权值和的积
        //好像有点绕╮(╯▽╰)╭...
        /*
            p1         /
                      /   p3            p1(p3+p4)+p2(p3+p4)=(p1+p2)*(p3+p4)
             p2      /
                    /    p4             手动绘图  ∩_∩)O哈哈哈~
                   /
        */
  1. 容易知道,直线两端任意两点权值积的和,就是两个区间权值和的积(代码里面画了图(想不到吧^_^))
  2. 首先按照极角对每个点进行排序
  3. 初始直线从y轴开始,把x>=0的点都加到右区间,x<0的点都加到左区间
  4. 直线逆时针旋转,每旋转到一个点,把这个点从它原来所在的区间里面剔除掉,加到另一个去见里面
  5. 保存最大值
#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;
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值