USACO6.4.2 Electric Fences(fence3)

“模拟退火”,也是胡整算法吧,不过确实很好用

题目范围不大,精度要求不高,枚举,将整个连续空间离散分成1000个每个相距0.1的点,枚举每个点,能找到答案,复杂度高,是可以优化的

二分,每次枚举9个点 ( 正方形平均的9个点 ),找到最有点后再以这个点为九个点的中心点缩小步长再尝试9个点,直到枚举的两点间相差<0.01

求出一个点到某线段的最短距离,要分两种情况:

一是这个点做垂线会落到这个线段上,这种情况用差乘求出这个点与线段构成的三角形面积*2,然后再除以这个线段的长度就是垂线的长度:

二是这个点对线段做出的垂线在线段外,那么最短的距离只可能是到线段两个端点距离的最短那个。判断钝角三角形(x1^2+x2^2<x3^2,代表x1与x2的夹角是钝角)


/*
PROG:fence3
ID:xsy97051
LANG:C++
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

#define INF 2000000005    

struct node  
{  
    double x0,y0,x1,y1,len;         
}	line[152];  
double ansx,ansy,ans,len,step;
double MaxX,MaxY,StartX,StartY; 

double dis(node a,double x,double y)  
{  
    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);  
   		else	return sqrt(x2);       
    }

    x1=x-a.x0;
    y1=y-a.y0;
    x2=a.x1-a.x0;
    y2=a.y1-a.y0;
    
    double tep=x1*y2-x2*y1;  
    if(tep<0)	tep=-tep;  
    
    return tep/a.len;  
}  

int main()
{
	freopen("fence3.in","r",stdin);
	freopen("fence3.out","w",stdout);
	StartX=StartY=INF;  
    MaxX=MaxY=-INF; 
    int n;
    cin>>n;
    for(int 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)  
            swap(line[i].x1,line[i].x0);   
        if(line[i].y1<line[i].y0)  
        	swap(line[i].y1,line[i].y0);  
        
        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=INF;
    ans*=ans;
    
    while(len>0.01)
    {
        step=len/4;  
        MaxX=StartX+len; 
		MaxY=StartY+len;
		for(double x=StartX;x<=MaxX;x+=step)  
            for(double y=StartY;y<=MaxY;y+=step)  
            {
				double now=0;
                for(int i=1;i<=n;i++)  
                {  
                    now+=dis(line[i],x,y);  
                    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、付费专栏及课程。

余额充值