2016.08.07计算几何总结测试day2

T1 bzoj: [Usaco2010 OPen]Triangle Counting 数三角形

看到这个题n那么大, 于是想到极角排序搞一搞,然而排完序后立马懵逼,完全不知道接下来应该怎么写。。。。

盯了好久题目给的图后全无思路于是手绘图,然后我就发现了秘密。。。。

极角排序后,如果两个点能与另外的某一个点构成黄金三角形,那么那个点必然在这两个点与原点连线的延长线所夹的区间内。

又因为有极角排序,点a[1],a[2]能构成的三角形,换成点a[1],a[3]肯定也可以构成,因为它们的区间一定是包含关系。

于是我们搞出所有a[i],a[i-1]区间内的答案,计算对答案的贡献即可。

正着有i-1个区间包含它,反着有n-i个区间包含它,然后搞一搞就好了。。。

细节什么的参见代码,反正感觉我代码是写丑了,我还算出了j搞了两遍for循环,看他们代码一个个巨短。。。。理应一遍就应该可以了吧。。。。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 #define maxn 100100
 8 #define pi (acos(-1.0))
 9  
10 int n,j,tmp,sum;
11 long long ans;
12  
13 struct point{
14     double x,y,ang;
15 }p[maxn];
16  
17 double operator *(point a,point b){return a.x*b.y-a.y*b.x;}
18  
19 bool cmp(point a,point b){
20     return a.ang<b.ang;
21 }
22  
23 int main(){
24 //  freopen("input.txt","r",stdin);
25 //  freopen("output.txt","w",stdout);
26     scanf("%d",&n);
27     for (int i=1;i<=n;i++)
28         scanf("%lf%lf",&p[i].x,&p[i].y),p[i].ang=atan2(p[i].y,p[i].x);
29     sort(p+1,p+n+1,cmp);
30     for (int i=1;i<=n;i++) if (p[i].ang-p[1].ang>pi){j=tmp=i;break;}
31     for (int i=2;i<j;i++){
32         int sum=0;
33         while (p[tmp].ang-p[i].ang<pi && tmp<=n) tmp++,sum++;
34         ans+=(long long)sum*(i-1)*(j-i);
35     }
36     for (int i=1;i<j;i++) if (p[j].ang-p[i].ang<pi){tmp=i;break;}
37     for (int i=j+1;i<=n;i++){
38         int sum=0;
39         while (p[i].ang-p[tmp].ang>pi && tmp<j) tmp++,sum++;
40         ans+=(long long)sum*(i-j)*(n-i+1);
41     }
42     printf("%lld
",ans);
43     return 0;
44 }
T1

T2 poj 3608 Bridge Across Islands 

两个多边形间的旋转卡壳,网上给了一大堆代码,一大堆题解,感觉都大同小异,几乎都是国外某大牛的论文翻译过来的。。。。。什么搞出一个ymin,ymax,特别是哪个叉乘while,根本看不懂。。。。。线段之间的距离真的可以用叉乘吗。。。。。(蒟蒻求解。。。)

于是我有一种想法,首先在多边形P上随便找到一条边,然后找到多边形Q上距离最近的点,然后将多边形P上的该边跳到它的邻边,同时直接移动多边形Q上找到的点,和两边的点比较,之后移动,直到移动到下一个点的距离比该点距离大就停止。然后在多边形Q上也做一次类似的即可。然后每次移动更新答案,就完了。。。。。

感觉这样做正确性也十分显然,因为对于任意一条边,另一个多边形的所有点距离它都是单峰的。于是对于当前点,它一定能找到距离最近的点。注意这里并不用两个指针扫,只需要将当前点与左右两边比较就行了。因为不可能出现左右两边的点都比当前点更优的情况,因为当前点一定是左右两边的某个点转移过来的,而对于当前的边,它一定会比至少一条边优。(这个性质稍微分情况讨论一下也可以得出),同时因为边每次只是跳到邻边,因此绝对不会出现每次移动n/2的情况,因为这个过程类似于旋转卡壳实现过程,时间复杂度也是可以保证的,常数好像大了一些。。。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 #define maxn 100010
 8 #define inf 1e9
 9 
10 int n,m;
11 double ans;
12 
13 struct point{
14     double x,y;
15 }p[maxn],q[maxn];
16 
17 struct line{
18     point from,to;
19 }lp[maxn],lq[maxn];
20 
21 point operator -(point a,point b){return (point){a.x-b.x,a.y-b.y};}
22 double operator *(point a,point b){return a.x*b.y-a.y*b.x;}
23 
24 double sqr(double x){return x*x;}
25 double dis(point a,point b){return sqr(a.x-b.x)+sqr(a.y-b.y);}
26 
27 double point_line_dis(point a,line b){
28     if (dis(b.to,b.from)+min(dis(a,b.to),dis(a,b.from))<max(dis(a,b.to),dis(a,b.from)))
29     return sqrt(min(dis(a,b.to),dis(a,b.from)));
30     else return fabs((b.to-a)*(b.from-a))/sqrt(dis(b.to,b.from));
31 }
32 
33 void solvep(){
34     double dist=inf;int pos;
35     for (int i=1;i<=m;i++){
36         double tmp=point_line_dis(q[i],lp[1]);
37         if (tmp<dist)
38             dist=tmp,pos=i;
39     }
40     ans=min(ans,dist);
41     for (int i=2;i<=n;i++){
42         while (point_line_dis(q[pos],lp[i])>point_line_dis(q[pos+1],lp[i])) pos==m?pos=1:pos++;
43         while (point_line_dis(q[pos],lp[i])>point_line_dis(q[pos-1],lp[i])) pos==1?pos=m:pos--;
44         ans=min(ans,point_line_dis(q[pos],lp[i]));
45     }
46 }
47 
48 void solveq(){
49     double dist=inf;int pos;
50     for (int i=1;i<=n;i++){
51         double tmp=point_line_dis(p[i],lq[1]);
52         if (tmp<dist)
53             dist=tmp,pos=i;
54     }
55     ans=min(ans,dist);
56     for (int i=2;i<=m;i++){
57         while (point_line_dis(p[pos],lq[i])>point_line_dis(p[pos+1],lq[i])) pos==n?pos=1:pos++;
58         while (point_line_dis(p[pos],lq[i])>point_line_dis(p[pos-1],lq[i])) pos==1?pos=n:pos--;
59         ans=min(ans,point_line_dis(p[pos],lq[i]));
60     }
61 }
62 
63 int main(){
64     while (scanf("%d%d",&n,&m)!=EOF){
65         ans=inf;
66         if (n==0 && m==0) break;
67         for (int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
68         for (int i=1;i<=m;i++) scanf("%lf%lf",&q[i].x,&q[i].y);
69         p[n+1]=p[1],q[m+1]=q[1];
70         for (int i=1;i<=n;i++) lp[i].from=p[i],lp[i].to=p[i+1];
71         for (int i=1;i<=m;i++) lq[i].from=q[i],lq[i].to=q[i+1];
72         solvep();
73         solveq();
74         printf("%.6f
",ans);
75     }
76     return 0;
77 }
T2

 T3 一道不知道哪里可以交的题目

题目大意:平面内有n个点,p1,p2,p3...pn。在平面内选m个点,并将p[]分成m段区间,定义d为每一段区间内的点p与找到的点q的距离的最大值,让所有区间d的最大值最小,并输出这个d。

看到最大值最小,考虑二分,二分这个答案d,然后考虑如何去check。

其次可以考虑贪心,因为已经得出了这个d,然后让这个区间内的所有点到找到的点的距离都不超过d,可以考虑最小圆覆盖,如果当前点在已经得到的圆内,就直接加,否则得到新的圆,如果新的圆的半径大于了这个二分的d,就可以新分一段,再去做最小圆覆盖,如果段数要大于m,就可以return 0了。

但以保证最小圆覆盖的复杂度必须要random_shuffle(),所以check()不能直接二分+最小圆覆盖,这样复杂度会爆炸,可以用倍增把二分的范围卡在logn之内再上二分就能(卡)过了。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 #define maxn 100010
  8 #define inf 2000000
  9 #define double long double
 10 const double eps=1e-9;
 11  
 12 int n,m;
 13  
 14 struct point{
 15     double x,y,r;
 16 }a[maxn],b[maxn],O;
 17  
 18 double sqr(double x){return x*x;}
 19 double dis(point a,point b){return sqr(a.x-b.x)+sqr(a.y-b.y);}
 20  
 21 void getcircle(int i,int j){
 22     O.r=dis(b[i],b[j])/4;
 23     O.x=(b[i].x+b[j].x)/2;
 24     O.y=(b[i].y+b[j].y)/2;
 25 }
 26  
 27 void getcircle(int i,int j,int k){
 28     double a,c,d,e,f,g;
 29     a=2*(b[i].x-b[j].x);
 30     g=2*(b[i].y-b[j].y);
 31     c=sqr(b[i].x)-sqr(b[j].x)+sqr(b[i].y)-sqr(b[j].y);
 32     d=2*(b[i].x-b[k].x);
 33     e=2*(b[i].y-b[k].y);
 34     f=sqr(b[i].x)-sqr(b[k].x)+sqr(b[i].y)-sqr(b[k].y);
 35     O.x=(c*e-g*f)/(a*e-g*d);
 36     O.y=(c*d-a*f)/(g*d-a*e);
 37     O.r=dis(O,b[i]);
 38 }
 39  
 40 bool incircle(int i){
 41     return dis(b[i],O)-O.r<=eps;
 42 }
 43  
 44 bool judgecircle(int l,int r,double limit){
 45 //    if (l==r) return 1;
 46     int cnt=0;
 47     for (int i=l;i<=r;i++) b[++cnt]=a[i];
 48     random_shuffle(b+1,b+cnt+1);
 49     getcircle(1,2);
 50     if (O.r>sqr(limit)+eps) return 0;
 51     for (int i=3;i<=cnt;i++)
 52         if (!incircle(i)){
 53             getcircle(1,i);
 54             if (O.r>sqr(limit)+eps) return 0;
 55             for (int j=2;j<i;j++)
 56                 if (!incircle(j)){
 57                     getcircle(i,j);
 58                     if (O.r>sqr(limit)+eps) return 0;
 59                     for (int k=1;k<j;k++)
 60                         if (!incircle(k)){
 61                             getcircle(i,j,k);
 62                             if (O.r>sqr(limit)+eps) return 0;
 63                         }
 64                 }
 65         }
 66     return 1;
 67 }
 68  
 69 bool check(double limit){
 70     int pos=0,num=0;
 71     for (int i=1;i<=n;i=pos+1){
 72         int len=1;
 73         while (i+(len<<1)-1<=n && judgecircle(i,i+(len<<1)-1,limit)) len<<=1;
 74         int l=i+len-1,r=min(i+(len*2)-1,n);
 75         while (l<r){
 76             int mid=(l+r)>>1;
 77             if (judgecircle(i,mid+1,limit)) l=mid+1;
 78             else r=mid;
 79         }
 80         pos=r,num++;
 81         if (num>m) return 0;
 82     }
 83     return 1;
 84 }
 85  
 86 int main(){
 87 //    freopen("input.txt","r",stdin);
 88 //    freopen("output.txt","w",stdout);
 89     scanf("%d%d",&n,&m);
 90     for (int i=1;i<=n;i++)
 91         scanf("%Lf%Lf",&a[i].x,&a[i].y);
 92     double l=0,r=inf;
 93     while (l+1e-7<=r){
 94         double mid=(l+r)/2;
 95         if (check(mid)) r=mid;
 96         else l=mid;
 97     }
 98     printf("%.6Lf
",l);
 99     return 0;
100 }
T3
原文地址:https://www.cnblogs.com/DUXT/p/5751729.html