zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    2018第一发:记一次【Advanced Installer】打包之旅
    Nginx 实现端口转发
    php支付宝手机网页支付类实例
    磁盘阵列操作实战
    错误修改/etc/fstab,导致系统无法开机
    linux 查看机器的cpu,操作系统等命令
    shell实现https登录
    linux tomcat配置https
    ArrayList和Vector以及synchronizedList
    java synchronized修饰普通方法,修饰静态方法,修饰代码块,修饰线程run方法 比较
  • 原文地址:https://www.cnblogs.com/DUXT/p/5751729.html
Copyright © 2011-2022 走看看