计算几何-UESTC 484 Division


题意:给一个萌点,找到经过这个萌点的射线可以平分多边形。


题解:
1、遍历角度会wa,需要遍历距离。因为枚举角度的话会用到三角函数,听说会损失精度。
2、二分的时候二分五六十次不然100次就差不多了,我把精度弄成了l和r相差10的-7次方就wa了。真2。
3、如何判断两个端点是不是在边上呢?!我一开始枚举斜率,这么损失精度的方法真是蠢死了。叉积:a X b = |a||b|sinα 。也就是说a和b的角度小于180的话,叉积大于0,大于180的话,叉积小于0。最右的端点和萌点的连线和其余点和萌点的连线叉乘一定全都大于0或者共线,最左端点和萌点的连线和萌点的连线叉乘一定全部大于0或者共线。
4、注意多边形的顶点。判断是否在线段上的时候算左边点不算右边点就好了。而且还要判一下交点是不是多边形顶点。
5、总结一下。先通过叉乘不小于0找到最右端点,再通过叉乘不大于0找到最左端点,做一条线段,遍历距离。然后得到一条直线和一个多边形。
6、5之后的方法1:通过叉乘找到所有在这条线右边的顶点,加入新多边形的点集,(算上共线),找到和线段的交点加入点集(不算上端点)。
7、5之后的方法2:记录端点号,把两个交点加入点集,以及从较小端点到较大端点的所有端点序号。(注意判一下是不是交点,如果已经加进去过了就别加了)。然后看看最右端点在不在集合中。以此来判断算出的多边形面积是啥
8、6或者7之后,得到新多边形面积,如果刚好是“多边形/2”就找到了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define SIZE_N 330
#define INF 0x3f3f3f3f
using namespace std;
const double PI = acos(-1);
const double eps = 1e-8;
const double eps1 = 1e-5;
int cmp(double x)
{
    if (fabs(x)< eps) return 0;
    if (x>0) return 1;
    return -1;
}
struct point
{
    double x,y;
    point(){}
    point (double a, double b): x(a),y(b) {}
    friend point operator + (const point &a, const point &b){
        return point(a.x + b.x, a.y + b.y);
    }
    friend point operator - (const point &a,const point &b){
        return point(a.x - b.x, a.y - b.y);
    }
    friend point operator * (const double &a, const point &b){
        return point(a * b.x, a * b.y);
    }
    friend point operator * (const point &a, const double &b){
        return point(a.x * b, a.y * b);
    }
    friend point operator / (const point &a, const double &b){
        return point(a.x/b, a.y/b);
    }
    friend bool operator == (const point &a, const point &b){
        return cmp(a.x-b.x) == 0 && cmp(a.y-b.y) == 0;
    }
}p[SIZE_N],ptemp[SIZE_N];
struct line
{
    point a,b;
}line1[SIZE_N];
double l,r;//从0到1二分距离
int N;
int left,right;//最左边点的序号和最右边点的序号

double ar;
double det(const point &a,const point &b)
{
    return a.x * b.y -a.y * b.x;
}

double dot(const point &a,const point &b)
{

    return a.x * b.x + a.y *b.y;
}

bool PointOnSegment (point p,point s,point t)
{//p在st上面!
    return cmp(det(p-s,t-s))== 0 && cmp(dot(p-s,p-t))<= 0;
}

bool parallel(line a, line b)
{
    return !cmp(det(a.a - a.b, b.a - b.b));
}

bool line_make_point(line a,line b, point &res)
{
    if (parallel(a,b)) return 0;
    double s1 = det(a.a-b.a, b.b - b.a);
    double s2 = det(a.b - b.a, b.b - b.a);
    res = (s1 * a.b - s2 * a.a)/(s1-s2);
    if(b.b == res )
        return 0;
    if (PointOnSegment(res,b.a,b.b))
        return 1;
    return 0;
}
int cmin,cmax;
point res1,res2;


double area(point a[],int n)
{
    double sum = 0;
    a[n] = a[0];
    for (int i = 0; i < n; i++){
        sum+= det(a[i],a[i+1]);
    }
    sum = fabs(sum);
    return sum/2.0;
}


double getarea(int cmin,int cmax)
{
    int num = 0;
    ptemp[num++] = res1;
    int isright = 0;
    for (int i = cmin; i < cmax; i++){
        ptemp[num++] = p[i];
        if (i == right)
            isright = 1;
    }
    if (!(res2 == p[cmax-1]) ){
        ptemp[num++] = res2;
    }
    if (isright == 1)
        return area(ptemp,num);
    else
        return ar - area(ptemp,num);
}

bool check(point P, point now)
{
    line li;
    li.a = P;
    li.b = now;
    int sum = 0;
    point res;
    int cmin = -1,cmax;
    for (int i = 1; i <= N; i++){
        if (line_make_point(li,line1[i],res)){
            sum++;
            if (sum == 1){
                res1 = res;
                cmin = i;
            }
            else{
                res2 = res;
                cmax = i;
            }
        }
    }
    if ( cmp(getarea(cmin,cmax )-(ar/2.0) )==-1)
        return 1;
    return 0;
}

double getdis(point p,point q)
{
    return sqrt((p.y-q.y)*(p.y-q.y) + (p.x-q.x)*(p.x-q.x));
}

double trans(double x)
{
    if(abs(x) < eps1) return abs(x);
    else return x;
}


void getlr(point P)
{
    for (int i = 0; i < N; i++){
        int isr = 0,isl = 0;
        for (int j = 0; j < N; j++){
            if (j != i){
                if (!(cmp(det(p[i]-P,p[j]-P) ) != -1))
                    isr = 1;
                else
                    isl = 1;
            }
        }
        if (isr != 1){
            right = i;
        }
        if (isl != 1)left = i;
    }
}

int main()
{
    //freopen("input.txt","r",stdin);
    int T;
    scanf("%d",&T);
    int cas = 1;
    while (T--){
        scanf("%d",&N);
        for (int i = 0; i < N; i++){
            scanf("%lf %lf",&p[i].x,&p[i].y);
            if (i != 0){
                line1[i].a = p[i-1];
                line1[i].b = p[i];
            }
        }ar = area(p,N);
        line1[N].a = p[N-1];
        line1[N].b = p[0];//从第1条线一直到第N条线
        point P;
        scanf("%lf %lf",&P.x,&P.y);

        point pr,pl;
        getlr(P);
        pr = p[right];
        pl = p[left];
        r = 1.0;
        l = 0;
        double res0;
        point resc;
        point now;
int a7 = 60;
        while (a7--){
            double mid = (r + l)/(2.0);
            now = pl + mid * (pr-pl);
            if (check(P,now)){
                resc = now;
                r = mid;
            }else
                l = mid;
        }
        double dis = getdis(now,P);
        printf("Case #%d: %.4f %.4f\n",cas++,trans((now.x-P.x)/dis+eps),trans((now.y-P.y)/dis+eps));
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值