USACO Section 5.2 Electric Fences - 有意思的枚举+计算几何

29 篇文章 0 订阅
10 篇文章 0 订阅

 

      这题一上来首先想到的是能否用数学方法来求得这个点..比如说画一个半径最小的圆使其与所有线段相交或相切…那么圆心就是所求..想法似乎没问题..但怎么来求是毫无头绪~想了良久也没想出用数学的方法如何实现…

      还是用枚举了…题目范围不大..并且精度要求不高..将整个( 0 , 0 ) ~ ( 100 ,100 ) 的连续空间离散分成1000个每个相距0.1的点..枚举每个点..定能找到答案..复杂度是 N*O(100^2)…估计最大数据时间大概需要十秒..发现这个时间是可以有优化空间的…我就直接用二分了..每次枚举9个点 ( 正方形平均的9个点 )…找到最有点后再以这个点为九个点的中心点缩小步长再尝试9个点..直道枚举的两点间相差<0.01..也就是步长<0.01…

      还有很重要的一个问题..枚举了一个点如何求出其到某线段的最短距离?..要分两种情况..第一种是这个点做垂线会落到这个线段上…这种情况用差乘求出这个点与线段构成的三角形面积*2…然后再除以这个线段的长度就是垂线的长度…而第二种情况是这个点对线段做出的垂线在线段外..那么最短的距离只可能是到线段两个端点距离的最短那个..如何判断这两种情况…其实出现第二种情况是因为这个点与线段构成的三角形是钝角三角形..并且不是这个点的两侧是钝角..是另外两个角有一个是钝角..如何判断钝角三角形..先求出三条边的长度..都知道坐标这个很好求…当x1^2+x2^2<x3^2…代表x1与x2的夹角是钝角…

 

Program:

/*  
ID: zzyzzy12   
LANG: C++   
TASK: fence3
*/      
#include<iostream>      
#include<istream>  
#include<stdio.h>     
#include<string.h>      
#include<math.h>      
#include<stack>
#include<map>
#include<algorithm>      
#include<queue>   
#define oo 2000000005  
#define ll long long  
#define pi (atan(2)+atan(0.5))*2 
using namespace std;
struct node
{
      double x0,y0,x1,y1,len;       
}line[152];
int i,n;
double x,y,ansx,ansy,ans,now,MaxX,MaxY,StartX,StartY,len,step;  
double dis(node a)
{
      double x1,y1,x2,y2;
      x1=(x-a.x0)*(x-a.x0)+(y-a.y0)*(y-a.y0);
      x2=(x-a.x1)*(x-a.x1)+(y-a.y1)*(y-a.y1);
      if (x1-x2-a.len*a.len>-0.001 || x2-x1-a.len*a.len>-0.001)  
      {         
              if (x1<x2) return sqrt(x1);
              return sqrt(x2);     
      }
      x1=x-a.x0; y1=y-a.y0;
      x2=a.x1-a.x0; y2=a.y1-a.y0;
      x1=x1*y2-x2*y1;
      if (x1<0) x1=-x1;
      return x1/a.len;
}
int main()  
{  
      freopen("fence3.in","r",stdin);   
      freopen("fence3.out","w",stdout);
      StartX=StartY=oo;
      MaxX=MaxY=-oo;
      scanf("%d",&n);
      for (i=1;i<=n;i++)
      {
             scanf("%lf%lf%lf%lf",&line[i].x0,&line[i].y0,&line[i].x1,&line[i].y1);
             line[i].len=sqrt((line[i].x0-line[i].x1)*(line[i].x0-line[i].x1)+(line[i].y0-line[i].y1)*(line[i].y0-line[i].y1));   
             if (line[i].x1<line[i].x0)
             {
                    x=line[i].x1; 
                    line[i].x1=line[i].x0;
                    line[i].x0=x;                          
             }
             if (line[i].y1<line[i].y0)
             {
                    y=line[i].y1;
                    line[i].y1=line[i].y0;
                    line[i].y0=y;                   
             }
             if (line[i].x1>MaxX) MaxX=line[i].x1; 
             if (line[i].x0<StartX) StartX=line[i].x0;
             if (line[i].y1>MaxY) MaxY=line[i].y1;
             if (line[i].y0<StartY) StartY=line[i].y0; 
      }
      if (MaxY>MaxX) len=MaxY/2;
         else len=MaxX/2; 
      ans=oo; ans*=ans;  
      while (len>0.01)
      {
              step=len/4;
              MaxX=StartX+len; MaxY=StartY+len;
              for (x=StartX;x<=MaxX;x+=step)
                 for (y=StartY;y<=MaxY;y+=step)
                 {
                        now=0;
                        for (i=1;i<=n;i++)
                        {
                               now+=dis(line[i]);
                               if (now>ans) break;    
                        }   
                        if (now<ans)
                        {
                               ans=now;
                               ansx=x; 
                               ansy=y;     
                        } 
                 }
              StartX=ansx-step/2;
              StartY=ansy-step/2; 
              len/=2; 
      }      
      printf("%.1lf %.1lf %.1lf\n",ansx,ansy,ans);
      return 0;     
}   


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值