您的位置:首页 > 产品设计 > UI/UE

HDU 4667 Building Fence(求凸包的周长)

2016-05-19 19:15 531 查看
A-BuildingFence
TimeLimit:1000MSMemoryLimit:65535KB64bitIOFormat:%I64d&%I64u
SubmitStatus

Description

Longlongago,thereisafamousfarmernamedJohn.Heownsabigfarmandmanycows.Therearetwokindsofcowsonhisfarm,oneisFriesian,andanotheroneisAyrshire.Eachcowhasitsownterritory.Indetail,theterritoryofFriesianisacircle,andofAyrshireisatriangle.Itisobviousthateachcowdoesn'twanttheirterritoryviolatedbyothers,sotheterritorieswon'tintersect.

Sincethewinterisfalling,FJhastobuildafencetoprotectallhiscowsfromhungrywolves,makingtheterritoryofcowsinthefence.Duetothefinancialcrisis,FJiscurrentlylackofmoney,hewantsthetotallengthofthefenceminimized.Sohecomestoyou,thegreatestprogrammereverforhelp.Pleasenotethatthepartoffencedon'thavetobeastraightline,itcanbeacurveifnecessary.

Input

Theinputcontainsseveraltestcases,terminatedbyEOF.Thenumberoftestcasesdoesnotexceed20.
EachtestcasebeginswithtwointegersNandM(0≤N,M≤50,N+M>0)whichdenotesthenumberoftheFriesianandAyrshirerespectively.ThenfollowsN+Mlines,eachlinerepresentingtheterritoryofthecow.EachofthefirstNlinescontainsthreeintegersXi,Yi,Ri(1≤Ri≤500),denotesthecoordinatesofthecircle'scentreandradius.TheneachoftheremainingMlinescontainssixintegersX1i,Y1i,X2i,Y2i,X3i,Y3i,denotesthecoordinatesofthetrianglevertices.Theabsolutevalueofthecoordinateswon'texceed10000.

Output

Foreachtestcase,printasinglelinecontainingtheminimalfencelength.Youroutputshouldhaveanabsoluteerrorofatmost1e-3.

SampleInput

11
441
000220

SampleOutput

15.66692

Hint

Pleaseseethesamplepictureformoredetails,thefenceishighlightedwithred.




题意:给定n个圆,m个三角形,求凸包的周长,详见上图。
题解:这个题有一种很水的做法就是把圆分成1000个点,然后直接对这些点求凸包。
   不推荐这个方法,属于水过的,换个精度高的数据没准就WA了,正确做法如下:
   把三角形的所有点视为单个点,标记三角形每个点的id为-1,-2,-3,-4.......放到一个集合P中,
   求这些点与圆的切线的交点,把这些点再放到集合P中,标记他们的id为圆的数组下标。
   再求圆与圆之间的内切线外切线与圆的交点,再放到集合P中,标记他们的id为圆的数组下标,
   把这些点进行凸包就可以求出周长了,id相同的点要算弧长。很多很多细节问题需要注意,已写到注释里。
   另外注意在没有三角形只有一个圆的情况下要单独考虑,因为不会通过点和圆之间
   的切线以及圆和圆之间的切线产生点,所以要单独判断然后输出圆的面积,
   continue即可。
   对于数组为什么开2W,目前未知,求大神解答。


#include<iostream>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stdlib.h>
#include<vector>
constdoublePI=acos(-1.0);
usingnamespacestd;
structPoint{
doublex,y;
intid;
Point(doublex=0,doubley=0,intid=-1):x(x),y(y){}//构造函数,方便代码编写
};
typedefPointVector;//从程序上实现,Vector只是Point的别名
structCircle{
Pointc;
doubler;
Circle(){}
Circle(Pointc,doubler):c(c),r(r){}
Pointpoint(doublea){
returnPoint(c.x+cos(a)*r,c.y+sin(a)*r);
}
};
//定义
#defineN50000//数组最小开20000为什么开20000没算对
//我算的是150+300+C(50,2)*4即三角形的点+这些点与圆的切点+圆之间的切点不到6000
//开大点就完了开500000才42344KB上限65536够用了
Pointp
;
Pointch
;
Circlec
;
Pointa[10],b[10];
Pointq
;
intm,n,t;
//点-点=向量
Vectoroperator-(PointA,PointB)
{
returnVector(A.x-B.x,A.y-B.y);
}
//运算符重载
booloperator<(constPoint&a,constPoint&b)
{
returna.x<b.x||(a.x==b.x&&a.y<b.y);
}
constdoubleeps=1e-10;
//三态函数精度问题
intdcmp(doublex)
{
if(fabs(x)<eps)return0;elsereturnx<0?-1:1;
}
booloperator==(constPoint&a,constPoint&b)
{
returndcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}
doubleDot(VectorA,VectorB)
{
returnA.x*B.x+A.y*B.y;
}
//求向量的值
doubleLength(VectorA)
{
returnsqrt(Dot(A,A));
}
//求夹角
doubleAngle(VectorA,VectorB)
{
returnacos(Dot(A,B)/Length(A)/Length(B));//a*b=|a|*|b|*cos(c)
}
//叉积
doubleCross(VectorA,VectorB)
{
returnA.x*B.y-A.y*B.x;
}
//求凸包模板
intConvexHull(Point*p,intn,Point*ch)//注意是*ch
{
sort(p,p+n);//这个要用到<重载运算符
n=unique(p,p+n)-p;//这个要用到==重载运算符
intm=0;
for(inti=0;i<n;i++)
{
//注意:可以共线时"<"改为"<="
while(m>1&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--;
ch[m++]=p[i];
}
intk=m;
for(inti=n-2;i>=0;i--)//注意是--不是++
{
while(m>k&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--;
ch[m++]=p[i];
}
if(n>1)m--;
returnm;//别忘了加m
}
Pointreadpoint()
{
doublex,y;
scanf("%lf%lf",&x,&y);
returnPoint(x,y);
}
voidreadcircle(Circle&c)
{
scanf("%lf%lf%lf",&c.c.x,&c.c.y,&c.r);
}
//点到圆的切线v存的是切点
intpcl(Pointp,Circleo,Vector*v)//点到圆的切线的切点,考虑不同情况
{
Vectoru=p-o.c;
doubled=Length(u);
doublea=atan2(u.y,u.x);//指向量u与x正半轴的夹角起点是圆心指向p点
if(d<o.r)return0;//圆内
elseif(dcmp(d-o.r)==0)//圆上
{
v[0]=o.point(a);
return1;
}
else//圆外两个外切点
{
doubleang=acos(o.r/d);
v[0]=o.point(a+ang);
v[1]=o.point(a-ang);
return2;
}
}
//两圆相离的时候的外公共切线
intccl(Circlec1,Circlec2,Point*a,Point*b)
{
intcnt=0;
if(dcmp(c1.r-c2.r)<0)//保证结果为正值
{
swap(c1,c2);
swap(a,b);
}

Vectoru=c2.c-c1.c;
doubled=Length(u);
doubleang=atan2(u.y,u.x);
//有外公切线
doubleg=acos((c1.r-c2.r)/d);
a[cnt]=c1.point(ang+g);
b[cnt]=c2.point(ang+g);
cnt++;
a[cnt]=c1.point(ang-g);
b[cnt]=c2.point(ang-g);
cnt++;
returncnt;
}
doublearc(Pointa,Pointb,Circlec)//a,b逆时针穿过圆c外面的的圆弧长
{
Pointu=a-c.c,v=b-c.c;
doubleang=Angle(u,v);//Angle是夹角模板中的angle=atan2(v.y,v.x)是极角
if(Cross(u,v)>eps)
returnang*c.r;
return(PI*2.0-ang)*c.r;
}
intmain()
{
while(scanf("%d%d",&n,&m)==2)
{
m=m*3;
for(inti=0;i<n;i++)//圆
readcircle(c[i]);
for(inti=0;i<m;i++)//三角形
{
p[i]=readpoint();
p[i].id=-i*3-1;//三角形的id标记成负数
}
if(n==1&&!m)
{
printf("%.5f\n",PI*2*c[0].r);;//输出一个圆的面积
continue;
}
//注意swap是把数值换位置n和m不换位置
for(inti=m-1;i>=0;i--)//三角形的点已经乘过3了
for(intj=0;j<n;j++)//圆
{
Pointv[2];//存放切点
intnum=pcl(p[i],c[j],v);//点到圆的切线p是点c是圆v是切点num是切点个数
for(intk=0;k<num;k++)
{
//从三角形的点中继续添加点
p[m]=v[k];//把切点放入点集中
p[m].id=j;//给新的点做标记这个点是第j个圆的
m++;
}
}
for(intj=0;j<n;j++)
for(inti=j+1;i<n;i++)
{
intnum=ccl(c[j],c[i],a,b);//圆和圆之间的外切点
for(intk=0;k<num;k++)
{
p[m]=a[k];
p[m].id=j;
m++;
p[m]=b[k];
p[m].id=i;
m++;
}
}
m=ConvexHull(p,m,ch);
memcpy(p,ch,sizeof(ch));
doublelen=0.0;
p[m]=p[0];
for(inti=0;i<m;i++)
{
if(p[i].id==p[i+1].id)//如果标记相等说明在一个圆上
{
len+=arc(p[i],p[i+1],c[p[i].id]);//两个点绕圆弧逆时针的长
}
else
{
len+=Length(p[i]-p[i+1]);//不在一个圆上输出算直线距离
}
}
printf("%.5f\n",len);
}
return0;
}





                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: