ZOJ 3716 Ribbon Gymnastics 解题报告

本文探讨了一道关于在四个固定点上绘制不重叠圆的问题,通过枚举法和几何运算找到最大半径之和。介绍了具体的算法实现过程及代码细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BUPT Summer training 1

题目

题意:

有四个点,在每个点画一个圆,互相不能重叠,求最大的半径和。

题解:

可知任意两条边的和有一个上限。必定至少有两条边的和等于上限,所以先枚举是哪两条边,如a和b,并且设a的值为x,则c和d的值也可得(c=min(len_ac-a,len_bc-len_ab+x),是一条折线)。那么a+b+c+d的值由x确定,由于函数由一些线段组成,所以根据x的取值区间可以得到最大值。

要注意的是:

1、某些ab和是不可能达到上限的。

2、c+d的值也不能超出限制。

//Time:0ms
//Memory:188KB
//Length:3078B
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#define EPS 1e-6
#define PI 3.1415926535898
#define INF 1000000000
#define MAXN 1010
#define MP(x,y) (make_pair((x),(y)))
#define FI first
#define SE second
using namespace std;
struct _line
{
    double x,c,a,b;
};
_line line[2][2],nline[8];
int xx[4],yy[4],top;
double len[4][4],l,r;
void addline(int h1,int h2,double x,double c,double a,double b)
{
    line[h1][h2].x=x,line[h1][h2].c=c;
    line[h1][h2].a=a,line[h1][h2].b=b;
    if(line[h1][h2].a>line[h1][h2].b)
        swap(line[h1][h2].a,line[h1][h2].b);
    line[h1][h2].a=max(line[h1][h2].a,l);
    line[h1][h2].b=min(line[h1][h2].b,r);
}
double cal(int a,int b)
{
    return sqrt(1.0*(xx[a]-xx[b])*(xx[a]-xx[b])+1.0*(yy[a]-yy[b])*(yy[a]-yy[b]));
}
void lineadd(int h11,int h12,int h21,int h22)
{
    _line tmpline;
    tmpline.x=line[h11][h12].x+line[h21][h22].x;
    tmpline.c=line[h11][h12].c+line[h21][h22].c;
    tmpline.a=max(line[h11][h12].a,line[h21][h22].a);
    tmpline.b=min(line[h11][h12].b,line[h21][h22].b);
    nline[top++]=tmpline;
}
double calans(int h,double a,double b)
{
    a=max(a,nline[h].a),b=min(b,nline[h].b);
    if(nline[h].a>nline[h].b-EPS)      return 0.0;
    if(a>b-EPS) return 0.0;
    return max((a*nline[h].x+nline[h].c),(b*nline[h].x+nline[h].c));
}
int main()
{
    //freopen("J:\\MyDocument\\Code\\input.txt","r",stdin);
    double ans,tmpans;
    while(scanf("%d%d",&xx[0],&yy[0])==2)
    {
        ans=-1e100;
        for(int i=1;i<4;++i)
            scanf("%d%d",&xx[i],&yy[i]);
        for(int i=0;i<4;++i)
            for(int j=i+1;j<4;++j)
                len[i][j]=len[j][i]=cal(i,j);
        for(int i=0;i<4;++i)
            for(int j=i+1;j<4;++j)
            {
                int a=i,b=j,c=-1,d=-1;
                for(int k=0;k<4;++k)
                    if(k!=i&&k!=j)
                    {
                        if(c==-1)   c=k;
                        else    d=k;
                    }
                l=max(EPS,max(len[a][b]-len[b][c],len[a][b]-len[b][d]));
                r=min(len[a][b],min(len[a][c],len[a][d]));
                if(l>r-EPS||r<EPS)  continue;

                addline(0,0,-1,len[a][c],len[a][c]+EPS,(len[a][c]+len[a][b]-len[b][c])*0.5);
                addline(0,1,1,len[b][c]-len[a][b],len[a][b]-len[b][c]+EPS,(len[a][c]+len[a][b]-len[b][c])*0.5);
                addline(1,0,-1,len[a][d],len[a][d]+EPS,(len[a][d]+len[a][b]-len[b][d])*0.5);
                addline(1,1,1,len[b][d]-len[a][b],len[a][b]-len[b][d]+EPS,(len[a][d]+len[a][b]-len[b][d])*0.5);
                top=0;
                tmpans=-1e100;
                for(int k=0;k<2;++k)
                    lineadd(0,k,1,0),lineadd(0,k,1,1);
                for(int k=0;k<top;++k)
                    tmpans=max(calans(k,l,r),tmpans);
                ans=max(ans,len[a][b]+min(tmpans,len[c][d]));
            }
        printf("%.8f\n",abs(ans));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值