hdu6127 (多校联合第七场) 几何 枚举

                                      HDU 6127

题目大意:

给出N个点的坐标,和每个点的一个值(记为val)。任意两个点构成的线段的价值为这两个点各自的val乘积

现在让你求出一条过原点的直线,使得这条直线通过的所有线段价值和最大(题目保证任意给出的两个点组成的直线不会通过原点)。

大致思路:

我们知道一条直线会将一个平面分成两部分(假设为A平面和B平面),那么所有的点就会分成两种,一种在A平面,一种在B平面。我们分别求出分布在A平面的点的val总和ans1和分布在B平面的点的val总和ans2,那么这条直线通过所有的线段价值和为ans1*ans2(相当于(a+b+c)*(a1+b1+c1)=a*a1+a*b1+a*c1+b*a1+b*b1+b*c1+c*a1+c*b1+c*c1)。

我们要寻找一条分割线,使得分割后的A平面的ans1乘上B平面的ans2最大。


如图所示,假设现在有6个点(1,2,3,4,5,6)。

我们可以看出x=0这条直线就将所有的点分成两部分了(A平面为x<0,B平面为x>0)。

定义变量sum为我们最终求得的结果。那么sum的初始值就为(val6+val5+val4)*(val1+val2+val3)(看图)。

我们现在就要找下一条分割线了,但是从那里找呢?

就像图上一样,我们可以把点1到原点的线段(假设为直线y1)当成下一条分割线,然后进行计算。 当选点1时,我们可以想象此时这条分割线为y1向右旋转了一丢丢。那么点1就跑到A平面去了,而B平面就没了点1。所以此时sum=

max(sum,(ans1+val1)*(ans2-val2));然后按照这个方法我们再找下一条分割线。。。

但是再选的话是选 点2还是点6呢? 如果我们不按照某种“顺序”依次找分割线的话,A平面会多或者少计算某些点,B平面也一样。。。

所以重要的来了。。我们可以将所有的点与原点构成的直线的斜率(也可以理解为极角)依次求出来,然后进行排序。当我们找分割线的时候,就依次从最大(最小)的斜率开始找(看图,其实就是要将可选的分割线进行转圈筛选)。然后每次算,每次维护sum,那么结果就是我们要求的。

代码如下:

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#define ll long long
#define eps 1e-8
using namespace std;
struct Point
{
    int x;
    int y;
    int val;
    double jiao;
} point[200005];
bool cmp(Point a,Point b)
{
    return a.jiao>b.jiao;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        ll lsum=0,rsum=0;
        for(int i=0; i<n; i++)
        {
            scanf("%d%d%d",&point[i].x,&point[i].y,&point[i].val);
            if(point[i].x<0)lsum+=(ll)point[i].val;//先将点进行划分
            else
                rsum+=(ll)point[i].val;
            point[i].jiao=atan(1.0*point[i].y/point[i].x);//计算出极角
        }
        ll sum=lsum*rsum;//初始值
        sort(point,point+n,cmp);//按照极角从大到小排序,也可以从小到大,但必须有方向,要不然会丢点。。
        for(int i=0; i<n; i++)//开始遍历
        {
            if(point[i].x<0)
            {
                rsum+=point[i].val;
                lsum-=point[i].val;
            }
            else
            {
                rsum-=point[i].val;
                lsum+=point[i].val;
            }
            sum=max(sum,rsum*lsum);//维护最大值
        }
        printf("%lld\n",sum);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值