hdu 4116 计算几何 + 扫描线

hdu 4116 计算几何 + 扫描线
题目描述:
   在无限平面上有N(N<1,000)个圆。问一条直线最多可以“穿过”几个圆,相切也算。

算法分析:
   
   这样的线一定是某两个圆的切线。但是枚举切线再O(N)判断的话肯定会T。

   于是我们枚举每个“中心圆”,求出这个圆与其他所有圆的切线。并极角排序。
   把每个切线想像成事件点,每个事件点要么增加一个圆,要么删除一个圆。

   这样的话对这个环行区间进行统计就可以了。
   
   求切线的时候,要判断中心圆与其他圆的关系: 包含,内切or外切,相交,被包含,分离。
   求切线其实只需要求一个角就可以了,不需要求完整的两点式。

#include<iostream>
#include<cassert>
#include<algorithm>
#include<cstdio>
#include<complex>
#include<cmath>
using  namespace std;
//  template
typedef complex< double> pnt;
typedef pair<pnt, double> circle;
const  int N = 1005;
const  double eps = 1e-10;
const  double pi = acos(-1.0);
inline  bool eq( double a,  double b){ return abs(b-a) < eps;}
inline  double fix( double arg) {
     while(arg > pi) arg -= 2*pi;
     while(arg <= -pi) arg += 2*pi;
     return arg;
}
circle num[N];
struct line {
     int id,c;
     double arg;
    line(){}
    line( int _id, int _c, double _arg) :
    id(_id) , c(_c), arg(_arg){
//         cout<<"add: "<<id<<" "<<arg<<" "<<c<<endl;
    }
     bool  operator < ( const line& A)  const{
         return eq(arg , A.arg) ? c > A.c : arg < A.arg;
    }
} Line[N<<2];
//  cut line
#define ht first
#define rs second
inline  void cut_line( const circle &A,  const circle &B,  int& ans,  int& cnt, const  int& id) {
     double d = abs(A.ht - B.ht) ;
     if(d <= abs(A.rs - B.rs)) {
         if(d <= B.rs - A.rs ) {ans ++;  return ;}
         if(eq(d , A.rs - B.rs) ) {
            Line[cnt++] = line(id, 1 , arg(B.ht - A.ht));
            Line[cnt++] = line(id,-1 , arg(B.ht - A.ht));
        }
         return ;
    }
     double t;
    t = acos((A.rs - B.rs)/ d);
     double ag = arg(B.ht - A.ht);
    Line[cnt++] = line(id, 1 , fix(ag-t));
    Line[cnt++] = line(id,-1 , fix(ag+t));
     if(d > A.rs + B.rs + eps) {
         double t = acos((A.rs + B.rs)/d);
        Line[cnt++] = line(id, 1 , fix(ag+t));
        Line[cnt++] = line(id,-1 , fix(ag-t));
    }
}
//  solve
bool vis[N];
int work( int n,  int len){
     int ans = 0, sum = 0;
     for( int i=0;i<n;i++){
        vis[i] = 0;
    }
     for( int i=0;i<len+len;i++) {
         int k = i%len;
         int id = Line[k].id;
         int c = Line[k].c;
         if(c == 1){
            assert(vis[id]==0);
            vis[id] = 1; sum ++;
        }
         else  if(c == -1) {
             if(vis[id] == 1)
                sum --, vis[id] = 0;
        }
         if(sum > ans ) ans = sum;
    }
     return ans;
}
//  main
int main(){
     int cas;
    cin >> cas;
     for( int oo=1; oo<= cas; oo++) {
         int n,len = 0;
        scanf("%d",&n);
         for( int i=0;i<n;i++) {
             int x,y,r;
            scanf("%d%d%d",&x,&y,&r);
            num[i] = make_pair(pnt(x,y) , r);
        }
         int ans = 0;
         for( int s = 0; s < n; s ++) {
//             cout<<"start: "<<s<<endl;
            len = 0;  int sum = 1;
             for( int i=0;i< n; i++)  if(i!=s) 
                cut_line(num[s] , num[i], sum, len, i);
            sort(Line, Line + len);
            sum += work(n,len);
             if(sum > ans) ans = sum;
        }
        printf("Case #%d: %d\n",oo, ans);
    }
}

你可能感兴趣的:(hdu 4116 计算几何 + 扫描线)