记录编号 238914 评测结果 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
题目名称 最小距离和 最终得分 100
用户昵称 GravatarSatoshi 是否通过 通过
代码语言 C++ 运行时间 26.268 s
提交时间 2016-03-19 15:26:11 内存使用 0.72 MiB
显示代码纯文本
#include <fstream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <iomanip>
#include <stack>
#include <time.h>
#define N 10010
using namespace std;
typedef long double ld;
ifstream in("space.in");
ofstream out("space.out");
int n,top;
ld ans=0,INF=ld(1<<28),Tans;
ld R=100,pi=acos(-1.0);
bool l[N]={0};
int fft[N]={0};//存储离模拟退火直线最近的100个点
class point
{
public:
	ld x,y;//横纵坐标
	void make(ld a,ld b)
	{
		x=a;
		y=b;
	}
	void print()
	{
		out<<x<<' '<<y<<endl;
	}
}po[N],P[11];
class line//直线的两点式方程:点A,B构成过A,B的直线,记做(A,B)
{
public:
	point A,B;
	void make(point a,point b)
	{
		A=a;B=b;
	}
}Orz,zrO,OOO;
class fuck//不要在意名字.......,name存的是原下标
{
public:
	ld dis;
	int name;
}AC[N];
bool operator <(fuck a,fuck b)
{
	return a.dis<b.dis;
}
point operator +(point a,point b)
{
	point c;
	c.x=a.x+b.x;
	c.y=a.y+b.y;
	return c;
}
point operator -(point a,point b)
{
	point c;
	c.x=a.x-b.x;
	c.y=a.y-b.y;
	return c;
}
point operator *(point a,ld b)
{
	a.x*=b;
	a.y*=b;
	return a;
}
point operator /(point a,ld b)
{
	a.x/=b;
	a.y/=b;
	return a;
}
ld operator *(point a,point b){return a.x*b.x+a.y*b.y;}
ld operator ^(point a,point b){return a.x*b.y-a.y*b.x;}
ld len(point a){return sqrt(a*a);}
ld dis(point a,point b)
{
	point c;
	c=a-b;
	return len(c);
}
void print(ld a,int b){out<<setprecision(b)<<std::fixed<<a<<endl;}
bool com(point a,point b)
{
	if(a.y<b.y)return 1;
	if(a.y==b.y)if(a.x<b.x)return 1;
	return 0;
}
void read()
{
	int i;
	in>>n;
	for(i=1;i<=n;i++)in>>po[i].x>>po[i].y;
}
ld getH(point a,point b,point c)//点c到两点式方程a,b的距离
{
	ld area=0;
	area=fabs((a-b)^(b-c));//底乘高=面积
	return area/dis(a,b);
}
ld sumdis(point a,point b)//统计这个两点式方程对所有点的距离和
{
	int i;
	ld sum=0;
	for(i=1;i<=n;i++)sum+=getH(a,b,po[i]);
	return sum;
}
int random(int l,int r)//生成[l,r]的随机数
{
	int s;
	s=l+rand()%(r-l+1);
	return s;
}
ld random_real(void){//生成一个[0,1]内的实数
	return ((((rand()<<15)|rand())&0x3fffffff)+0.0)/(0x3fffffff+0.0);
}
ld luangao(point S,point T)//对平移量进行模拟退火,这是一个向量方程,由位置向量S和方向向量T构成
{
	int i;
	point NS,NT,O;
	ld step,best=INF,dir;
	ld temp,Te,p;
	for(step=10000,Te=1000;step>=0.001;step/=2,Te/=2)//step为步长,Te为温度
	{
		O=S;
		if(Te>1)Te=1;
		for(dir=-1;dir<=1;dir+=2)//把直线向上平移还是向下平移
		{
		    NS.make(S.x+step*dir,0);
			NT=NS+T;//以位置向量NS,方向向量T,构建(NS,NT)为新的两点式方程
			temp=sumdis(NS,NT);//计算距离
			if(temp<best)//更优解直接更新
			{
				best=temp;
				O=NS;
				zrO.make(NS,NT);//存储直线
			}
			else//不太优的解以一定概率更新
			{
				p=exp((best-temp)/Te);//更新概率(p=e^(-delta/T))
				if(random_real()<=p)
				{
					best=temp;
				    O=NS;
					zrO.make(NS,NT);//存储直线
				}
			}
		}
		S=O;
	}
	return best;
}
void work()
{
	int i,j,m=4,rep;
	ld angle,angles,angleo,temp,step,dir,best,Te,p;
	point S,T;
	
	ans=best=INF;
	angle=angles=angleo=0;
	
	for(i=1;i<=m;i++)//生成初始位置向量
	{
		P[i].x=random(0,10000);
		P[i].y=random(0,10000);
	}
	for(i=1;i<=m;i++)
	{
		angle=angles=angleo=random(0,360);//随机初始极角
		best=INF;
	    for(step=100,Te=100000;step>=0.001;step/=2,Te/1.8)
	    {
		    angleo=angles;
			if(Te>1)Te=1;
			for(dir=-1;dir<=1;dir+=2)//顺时针还是逆时针旋转极角
		    {
		        angle=angles+step*dir*pi/180;//产生新极角
		        T.make(R*cos(angle),R*sin(angle));//R=100(可以调整),把极角转化为直线的方向向量T=(Rcosa,Rsina)
		        temp=luangao(P[i],T);//位置向量P[i],方向向量T产生的两点式方程为(P[i],P[i]+T)
			    if(best>temp)//以下作用和上文相同
			    {
				    best=temp;
				    angleo=angle;
					Orz=zrO;
			    }
				else
			    {
				    p=exp((best-temp)/Te);
				    if(random_real()<=p)
				    {
					    best=temp;
				        angleo=angle;
						Orz=zrO;
				    }
			    }
		    }
		    angles=angleo;
	    }
		if(ans>best)
		{
			ans=best;
			OOO=Orz;
		}
	}//OOO存储的是最优直线
	Tans=ans;
	for(i=1;i<=n;i++)//求出离这条直线最近的100个点
	{
		AC[i].dis=getH(OOO.A,OOO.B,po[i]);
		AC[i].name=i;
	}
	sort(AC+1,AC+n+1);//排序
	for(i=1;i*i*n<=1000000&&i<=n;i++)fft[++top]=AC[i].name;
}
void baoli()
{
	int i,j,k;
	ld temp=0;
	point S,T;
	ans=INF;
	for(i=1;i<=top;i++)//再使用100^3的暴力
	{
		for(j=i+1;j<=top;j++)
		{
			temp=sumdis(po[fft[i]],po[fft[j]]);
			if(ans>temp)ans=temp;
		}
	}
	Tans=fmin(Tans,ans);
	print(Tans,2);
}
int main()
{
	int start,end;
	start=clock();
	srand(time(NULL));
	read();
	work();
	baoli();
	end=clock();
	return 0;
}