poj-1379 Run Away

75 篇文章 0 订阅
30 篇文章 0 订阅

题意:

给出一个矩形作为边界,内部有n个点;

求一个点到这些点距离的最小值最大;

n<=1000;


题解:

最小值最大——二分答案!

然而并不能做到= =

正解似乎是一个叫做Voronoi的神奇东西;

不过写个20k的精确算法不如秒个2k的骗分算法A掉它嘛;

这道题我写的就是模拟退火;

首先设中点为起点似乎比较美观吧;

然后就是普通的退火过程,照着百度百科扒一扒那几句话就好;

(1) 初始化:初始温度T(充分大),初始解状态S(是算法迭代的起点), 每个T值的迭代次数L
(2) 对k=1,……,L做第(3)至第6步:
(3) 产生新解S′
(4) 计算增量Δt′=C(S′)-C(S),其中C(S)为评价函数
(5) 若Δt′<0则接受S′作为新的当前解,否则以概率exp(-Δt′/(KT))接受S′作为新的当前解(k为波尔兹曼常数).
(6) 如果满足终止条件则输出当前解作为最优解,结束程序。
(7) T逐渐减少,且T->0,然后转第2步。

OI里终止条件一般视为T<EPS就好;

不卡精度不卡时,良心一A题;

感觉自己成为欧洲人了23333;


代码:


#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 11000
using namespace std;
const double EPS=1e-10;
const double INF=1e100;
struct Point
{
	double x,y;
	Point(){}
	Point(double _,double __):x(_),y(__){}
	void read()
	{
		scanf("%lf%lf",&x,&y);
	}
	friend Point operator +(Point a,Point b)
	{
		return Point(a.x+b.x,a.y+b.y);
	}
	friend Point operator -(Point a,Point b)
	{
		return Point(a.x-b.x,a.y-b.y);
	}
	friend double operator *(Point a,Point b)
	{
		return a.x*b.x+a.y*b.y;
	}
	friend double dis2(Point a,Point b)
	{
		return (a-b)*(a-b);
	}
}p[N];
double X,Y;
int n;
double drand()
{
	return rand()%10000/10000.0;
}
double calc(Point a)
{
	double ret=INF;
	for(int i=1;i<=n;i++)
	{
		ret=min(ret,dis2(a,p[i]));
	}
	return -ret;
}
void slove(Point& ans,double T)
{
	Point next,dt;
	double E,nE,dE;
	while(T>EPS)
	{
		dt=Point((drand()*2-1.0)*T,(drand()*2-1.0)*T);
		next=ans+dt;
		next.x=min(X,max(0.0,next.x)),next.y=min(Y,max(0.0,next.y));
		nE=calc(next);
		dE=nE-E;
		if(dE<0||drand()<exp(-dE/T))
			ans=next,E=nE;
		T*=0.99140142;
	}
}
int main()
{
	srand(140142);
	int c,T,m,i,j,k;
	Point ans;
	scanf("%d",&T);
	for(c=1;c<=T;c++)
	{
		scanf("%lf%lf%d",&X,&Y,&n);
		for(i=1;i<=n;i++)
			p[i].read();
		ans=Point(X/2,Y/2);
		slove(ans,max(X,Y)/2);
		printf("The safest point is (%.1lf, %.1lf).\n",ans.x,ans.y);
	}
	return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值