HDU 3982 半平面交+圆和凸多边形面积并

从这里看到的这个题。。很容易想到正解。

http://blog.csdn.net/zxy_snow/article/details/6739561

 

思路大概一样,就是不知道为什么我被卡精度了,,,

acos精度本来就不好,然后题目还要求输出百分比+五位小数,直接把精度卡了。

反正我写出来的当半径很大的时候误差会非常大,会达到3%左右。哎,查了一个下午,用几何画板模拟。真是恶心死了!

我早知道卡精度就不做了。

不知道“小媛”姐姐是怎么做的,实在不想看这个题了。

 

感觉我的思维和代码都是挺清晰的。。

尽管wa了,还是贴出来吧。。

 

View Code
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <algorithm>
  5 #include <cmath>
  6 #include <iostream>
  7 
  8 #define N 222222
  9 #define SIDE 11111
 10 #define EPS 1e-6
 11 #define BUG system("pause")
 12 
 13 const double PI=acos(-1.0);
 14 
 15 using namespace std;
 16 
 17 struct PO
 18 {
 19     double x,y,ag;
 20     bool fg;
 21     void prt() {printf("%lf     %lf    %d\n",x,y,fg);}
 22 }p[N],o,my,dp[N];
 23 
 24 struct LI
 25 {
 26     PO a,b; double ag;
 27     void in(double x1,double y1,double x2,double y2)
 28     {a.x=x1; a.y=y1; b.x=x2; b.y=y2;}
 29     void prt() {printf("%lf     %lf     %lf     %lf     %lf\n",a.x,a.y,b.x,b.y,ag);}
 30 }li[N],deq[N],qs;
 31 
 32 double r,ah;
 33 int n,cnt,tn,m;
 34 
 35 inline int dc(double x)
 36 {
 37     if(x>EPS) return 1;
 38     else if(x<-EPS) return -1;
 39     return 0;
 40 }
 41 
 42 inline PO operator -(PO a,PO b)
 43 {
 44     PO c; c.x=a.x-b.x; c.y=a.y-b.y;
 45     return c;
 46 }
 47 
 48 inline PO operator +(PO a,PO b)
 49 {
 50     PO c; c.x=a.x+b.x; c.y=a.y+b.y;
 51     return c;
 52 }
 53 
 54 inline PO operator *(PO a,double k)
 55 {
 56     a.x*=k; a.y*=k;
 57     return a;
 58 }
 59 
 60 inline PO operator /(PO a,double k)
 61 {
 62     a.x/=k; a.y/=k;
 63     return a;
 64 }
 65 
 66 inline double cross(PO a,PO b,PO c)
 67 {
 68     return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
 69 }
 70 
 71 inline double dot(PO a,PO b,PO c)
 72 {
 73     return (b.x-a.x)*(c.x-a.x)+(b.y-a.y)*(c.y-a.y);
 74 }
 75 
 76 inline double getlen(const LI &a)
 77 {
 78     return sqrt((a.b.x-a.a.x)*(a.b.x-a.a.x)+(a.b.y-a.a.y)*(a.b.y-a.a.y));
 79 }
 80 
 81 inline double getlen(const PO &a)
 82 {
 83     return sqrt(a.x*a.x+a.y*a.y);
 84 }
 85 
 86 inline double getdis(const PO &a,const PO &b)
 87 {
 88     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
 89 }
 90 
 91 inline double getdisl(const PO &a,const LI &b)
 92 {
 93     PO t1=a-b.a,t2=a-b.b; 
 94     return fabs(cross(o,t1,t2))/getlen(b);
 95 }
 96 
 97 inline void read()
 98 {
 99     scanf("%lf%d",&r,&tn);
100     double a,b,c,d;
101     n=0;
102     for(int i=1;i<=tn;i++)
103     {
104         ++n;
105         scanf("%lf%lf%lf%lf",&li[i].a.x,&li[i].a.y,&li[i].b.x,&li[i].b.y);
106         if(dc(getdisl(o,li[i])-r)>0) n--; 
107     }
108     scanf("%lf%lf",&my.x,&my.y);
109 }
110 
111 inline void getdir()//规范每条线的方向 
112 {
113     for(int i=1;i<=n;i++)
114         if(dc(cross(li[i].a,li[i].b,my))<0) swap(li[i].a,li[i].b);
115 }
116 
117 inline void getangle()//计算所有直线的极角 
118 {
119     for(int i=1;i<=n;i++)
120         li[i].ag=atan2(li[i].b.y-li[i].a.y,li[i].b.x-li[i].a.x);
121     li[n+1].in(-SIDE,-SIDE,SIDE,-SIDE);
122     li[n+2].in(SIDE,-SIDE,SIDE,SIDE);
123     li[n+3].in(SIDE,SIDE,-SIDE,SIDE);
124     li[n+4].in(-SIDE,SIDE,-SIDE,-SIDE);
125     for(int i=n+1;i<=n+4;i++) li[i].ag=atan2(li[i].b.y-li[i].a.y,li[i].b.x-li[i].a.x);
126     n+=4;
127 }
128 
129 inline bool cmp(const LI &a,const LI &b)//极角排序,内侧在前 
130 {
131     if(dc(a.ag-b.ag)==0) return dc(cross(a.a,a.b,b.a))<0;
132     return a.ag>b.ag;
133 }
134 
135 inline PO getpoint(const LI &a,const LI &b)//直线交点 
136 {
137     double k1=cross(a.a,b.b,b.a),k2=cross(a.b,b.a,b.b);
138     PO c;
139     c=a.a+(a.b-a.a)*k1/(k1+k2);
140     return c;
141 }
142 
143 inline void getcut()//半平面交 
144 {
145     sort(li+1,li+1+n,cmp);
146     m=1;
147     for(int i=2;i<=n;i++)
148         if(dc(li[i].ag-li[m].ag)!=0)
149             li[++m]=li[i];
150     deq[1]=li[1]; deq[2]=li[2];
151     int bot=1,top=2;
152     for(int i=3;i<=m;i++)
153     {
154         while(bot<top&&dc(cross(li[i].a,li[i].b,getpoint(deq[top],deq[top-1])))<0) top--;
155         while(bot<top&&dc(cross(li[i].a,li[i].b,getpoint(deq[bot],deq[bot+1])))<0) bot++;
156         deq[++top]=li[i];
157     }
158     while(bot<top&&dc(cross(deq[bot].a,deq[bot].b,getpoint(deq[top],deq[top-1])))<0) top--;
159     while(bot<top&&dc(cross(deq[top].a,deq[top].b,getpoint(deq[bot],deq[bot+1])))<0) bot++;
160     if(bot==top) return;
161     cnt=0;
162     for(int i=bot;i<top;i++) p[++cnt]=getpoint(deq[i],deq[i+1]);
163     if(top-1>bot) p[++cnt]=getpoint(deq[bot],deq[top]);
164 }
165 
166 inline double getg(const PO &s,const PO &t)//弓形面积 
167 {
168     double res;
169     double cc=dot(o,s,t)/getlen(s)/getlen(t);//夹角的余弦值,不能用正弦 asin的范围是[-π/2, π/2] acos的范围是[0, π] 
170     cc=acos(cc);
171     if(dc(cross(o,s,t))<0) {cc=2*PI-cc;res=fabs(cross(o,s,t))/2;} //三角形 
172     else res=-fabs(cross(o,s,t)/2);
173     double la=r*cc; //弧长 
174     double ss=r*la/2;//扇形面积 
175     return res+ss;
176 }
177 
178 inline PO getf(const PO &a,const PO &b)//ab的右旋法向量 
179 {
180     PO zt=b-a,rt;
181     rt.x=zt.y; rt.y=-zt.x;
182     return rt;
183 }
184 
185 inline PO rotate(const PO &a,double sss,double ccc)
186 {
187     PO rt;
188     rt.x=a.x*ccc-a.y*sss;
189     rt.y=a.x*sss+a.y*ccc;
190     return rt;
191 }
192 
193 inline void getcpoint(const LI &a,PO &s,PO &t)//直线与圆的交点 
194 {
195     double h=getdisl(o,a);
196     double tt=sqrt(r*r-h*h);
197     double sss=tt/r,ccc=h/r;
198     PO f;
199     if(dc(cross(o,a.a,a.b))<0) f=getf(a.b,a.a);//判断顺逆时针旋转 
200     else f=getf(a.a,a.b);
201     f=f/getlen(a)*r;
202     s=rotate(f,-sss,ccc);
203     t=rotate(f,sss,ccc);
204 }
205 
206 inline bool onseg(const PO &a,const LI &b)//a在直线b上,求a是否在线段b上 
207 {
208     PO s1=b.a-a,s2=b.b-a;
209     if(dc(dot(o,s1,s2))<=0) return true;
210     return false;
211 }
212 
213 inline bool cmp1(const PO &a,const PO &b)
214 {
215     return a.ag<b.ag;
216 }
217 
218 inline bool cmp2(const PO &a,const PO &b)//unique的去重函数 
219 {
220     if(dc(a.ag-b.ag)==0) return true;
221     return false;
222 }
223 
224 inline void cir()
225 {
226     for(int i=1;i<=(cnt>>1);i++) swap(p[i],p[cnt-i+1]);//半平面交是顺时针的,转换为逆时针 
227     n=0;
228     for(int i=1;i<=cnt;i++)
229     {
230         int dis=dc(getdis(o,p[i])-r);
231         if(dis==-1)//凸多边形在圆内的顶点 
232         {
233             dp[++n]=p[i];
234             dp[n].fg=0;
235         }
236         else if(dis==0)//在圆上的顶点 
237         {
238             dp[++n]=p[i];
239             dp[n].fg=1;
240         }
241     }
242     p[cnt+1]=p[1];
243     PO s,t;LI pl;
244     for(int i=1;i<=cnt;i++)//凸多边形和圆的交点 
245     {
246         pl.a=p[i]; pl.b=p[i+1];
247         if(dc(getdisl(o,pl)-r)<0)//在圆内 
248         {
249             getcpoint(pl,s,t);
250             if(onseg(s,pl))
251             {
252                 dp[++n]=s;
253                 dp[n].fg=1;
254             }
255             if(onseg(t,pl))
256             {
257                 dp[++n]=t;
258                 dp[n].fg=1;
259             }
260         }
261     }
262     for(int i=1;i<=n;i++) dp[i].ag=atan2(dp[i].y-my.y,dp[i].x-my.x);
263     sort(dp+1,dp+1+n,cmp1); 
264     n=unique(dp+1,dp+1+n,cmp2)-dp-1;
265     dp[n+1]=dp[1];
266     ah=0;
267     for(int i=1;i<=n;i++)
268         if(dp[i].fg&&dp[i+1].fg) ah+=getg(dp[i],dp[i+1]);
269 }
270 
271 inline double getarea()
272 {
273     double res=0;
274     dp[n+1]=dp[1];
275     for(int i=1;i<=n;i++) res+=cross(o,dp[i],dp[i+1]);
276     return res/2;
277 }
278 
279 inline void go()
280 {
281     if(n==0)
282     {
283         printf("100.00000%%\n");
284         return;
285     }
286     getdir();
287     getangle();
288     getcut();
289     cir();
290     double area=fabs(getarea());
291     printf("%.5lf%%\n",(area+ah)/(PI*r*r)*100);
292     
293 }
294 
295 int main()
296 {
297     int cas; scanf("%d",&cas);
298     for(int i=1;i<=cas;i++)
299     {
300         printf("Case %d: ",i);
301         read(),go();
302     }
303     return 0;
304 }

(至今写的最长的计算几何题了。。。)

 

 

 

转载于:https://www.cnblogs.com/proverbs/archive/2013/03/02/2940388.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值