码迷,mamicode.com
首页 > 其他好文 > 详细

XJOI xtx的游戏(计算几何)

时间:2017-09-08 20:38:51      阅读:260      评论:0      收藏:0      [点我收藏+]

标签:几何   返回值   printf   半径   idt   operator   向量   ++   eve   

技术分享

技术分享

技术分享

题意非常清真,给你一堆点和一个圆半径,选一个圆心让覆盖的点的点权和最大

首先,傻傻的做法,暴力微分枚举点,暴力统计答案.玄学复杂度

这个傻傻的代码虽然不是重点,还是贴一张图片吧

技术分享

然后开始糊正解,正解,正解!

思考一个圆覆盖的点集,肯定可以通过某种变换让两个点在圆上且包含的点不变

分类讨论:

1.两个点刚开始就在圆上

2.一个点在圆上,绕该点旋转圆,第一个点碰到边界时停下,转化为1

3.无点在圆上,先平移,第一个点碰到边界时停下,转化为2

于是,上述结论证明了.

/-----------------------------------------------/

那么只需要枚举两个点,因为知道半径,所以就可以算出两个圆心作为候选答案,然后暴力统计答案即可

复杂度n^3

/-----------------------------------------------/

然而,我却没有用这种方法,而是学习了zrf的方法,因为这种方法更好写且复杂度更优,仅为n^2logn,

首先,思考一个圆覆盖的点集,肯定可以通过某种变换让至少一个点在圆上且包含的点不变

这就不证明了,跟上面的证明如出一辙

那么就先枚举这个点

再使用扫描线做法,统计出每个可能被覆盖的点开始被覆盖的弧度和离开圆的弧度,然后排一遍序,一边扫过去统计答案.

如图技术分享

然后就非常简单了

 

技术分享
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=210;
const double pi=3.14159265358979323,eps=1e-12;
struct vec{
    double x,y;
    inline vec(double x=0,double y=0):x(x),y(y){};
}a[N];
inline vec operator -(vec x,vec y){
    return vec(x.x-y.x,x.y-y.y);
}
inline double dot(vec x,vec y){
    return x.x*y.x+x.y*y.y;
}
inline int dcm(double x,double y){
    return fabs(x-y)<eps?0:x>y?1:-1;
}
vector<pair<double,int> >g;
int n,w[N];
double r;
ll ans;
ll solve(const int x){
    g.clear();
    ll res=0,ttt=0;
    for (int i=1; i<=n; i++)
    if (i!=x){
        vec t=a[i]-a[x];
        double dis=sqrt(dot(t,t)),now,oth;
        if (dcm(dis,2*r)==1) continue;
        now=atan2(t.y,t.x);
        oth=acos(dis/(2*r));
        g.push_back(make_pair(now-oth,w[i]));
        g.push_back(make_pair(now+oth,-w[i]));
    }
    sort(g.begin(),g.end());
    for (vector<pair<double,int> >::iterator it=g.begin(); it!=g.end(); it++){
        ttt+=(*it).second;
        res=max(res,ttt);
    }
    return res+w[x];
}
int main(){
    scanf("%lf%d",&r,&n);
    for (int i=1; i<=n; i++) scanf("%lf%lf%d",&a[i].x,&a[i].y,&w[i]);
    for (int i=1; i<=n; i++) ans=max(ans,solve(i));
    printf("%lld",ans);
}
View Code

 

/------------------------------------------------/

但是还有一个事情没有说,

那就是彩蛋

那就是atan2函数

atan2(double y,double x)是系统自带的,精度比atan要好很多(注意是先y后x)

因为他计算时如果fabs(x)>fabs(y)那么算的时候就会使用atan(y/x)而不是atan(x/y)

然而他又不完全等于atan

他的返回值在(-pi,pi)之间

那么是什么含义呢

那就是原点向右的水平向量作为始边(x轴)

以逆时针为正方向

原点出发的向量(x,y)作为终边的角度值

于是,这玩意是极角排序神器,不仅精度高,而且方便

比如说,你要对n个点进行极角排序,可以不用差积,任意选一个点作为一个原点直接按照atan2值排序

就得到了第三象限->第四象限->第一象限->第二象限的一个序列

XJOI xtx的游戏(计算几何)

标签:几何   返回值   printf   半径   idt   operator   向量   ++   eve   

原文地址:http://www.cnblogs.com/Yuhuger/p/7495994.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!