POJ 3384 || Feng Shui (半平面交内推R

题目大意:给你一个凸包,里面放两个半径相同的圆,问你如果让这两个圆覆盖凸包的面积最大。

两个圆可以覆盖重合,但不是不会跑到墙角折起来。输出放置圆的两个圆的圆心坐标。如果有多种可能,输出任意一种。

思路:

半平面交内推R,求目前这个核最远的两点,因为半径固定,两个圆的圆心离的越开,你们覆盖的凸包的面积也就越大。

        1.半平面交内推R

        2.暴力扫描一边核(就是一个新的凸包)的点,算出距离最远的两个点,就输出。

代码:78ms c++

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-8;
struct pnode{
    double x,y;
    pnode(double xx=0.0,double yy=0.0):x(xx),y(yy){}
    //绕原点旋转角度a(弧度)
    void tansxy(double a){
        double tx = x,ty= y;
        x = tx*cos(a) - ty*sin(a);
        y = tx*sin(a) + ty*cos(a);
    }

}p[105],tp[105],q[105];
double dist(pnode p1,pnode p2){
    return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
//通过两点,确定直线方程
void get_line( pnode p1,pnode p2,double &a,double &b,double &c){
    a = p2.y - p1.y;
    b = p1.x - p2.x;
    c = p2.x*p1.y - p1.x*p2.y;
}

//求交点
pnode line_inter_point(pnode p1,pnode p2,double a,double b,double c){
    double u = fabs(a*p1.x+b*p1.y+c);
    double v = fabs(a*p2.x+b*p2.y+c);
    pnode t;
    t.x = ( p1.x*v + p2.x*u )/(u+v);
    t.y = ( p1.y*v + p2.y*u )/(u+v);
    return t;
}

//顺时针,加入一条边,切割
void cut( pnode p[],int &cnt,double a,double b,double c){
    int tmp = 0;
    for( int i = 1;i<= cnt;++i){
        //当前点在右侧 逆时针为在左侧 改为  if( <eps )
        if( a*p[i].x + b*p[i].y + c > -eps)
            tp[++tmp] = p[i];
        else{
            if( a*p[i-1].x + b*p[i-1].y +c > eps )
                tp[++tmp] = line_inter_point(p[i-1],p[i],a,b,c);
            if( a*p[i+1].x + b*p[i+1].y +c > eps )
                tp[++tmp] = line_inter_point(p[i],p[i+1],a,b,c);
        }
    }
    for( int i = 1; i <= tmp ;++i)
        p[i] = tp[i];
    p[0] = p[tmp];
    p[tmp+1] = p[1];
    cnt = tmp;
}
void solve(pnode q[],int n,double r){

    q[0]=q[n];
    q[n+1]=q[1];
    for(int i=0;i<=n+1;i++) p[i]=q[i];
    int cnt=n;
    for(int i=1;i<=n;i++){
        double a,b,c;
        pnode p1( q[i+1].y-q[i].y ,q[i].x-q[i+1].x);
        double k=r/sqrt(p1.x*p1.x+p1.y*p1.y);
        p1.x=k*p1.x;p1.y=p1.y*k;
        pnode p2 (p1.x+q[i].x,p1.y+q[i].y );
        pnode p3 (p1.x+q[i+1].x,p1.y+q[i+1].y );
        get_line(p2,p3,a,b,c);
        cut(p,cnt,a,b,c);
    }
    double maxdist=0,d;
    pnode p1,p2;
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=cnt;j++){
            d=dist(p[i],p[j]);
            if(d+eps>maxdist){
                maxdist=d;
                p1=p[i];
                p2=p[j];
            }
        }
    printf("%.4lf %.4lf %.4lf %.4lf\n",p1.x,p1.y,p2.x,p2.y);
}
int main()
{
    int n,r;
    while( ~scanf("%d %d",&n,&r)){
        for( int i = 1;i <= n;++i)
            scanf("%lf %lf",&q[i].x,&q[i].y);
        solve(q,n,r);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值