模拟退火(bzoj 3680: 吊打XXX)

3680: 吊打出题人

Time Limit: 10 Sec   Memory Limit: 128 MBSec   Special Judge
Submit: 3210   Solved: 1209
[ Submit][ Status][ Discuss]

Description

gty又虐了一场比赛,被虐的蒟蒻们决定吊打gty。gty见大势不好机智的分出了n个分身,但还是被人多势众的蒟蒻抓住了。蒟蒻们将n个gty吊在n根绳子上,每根绳子穿过天台的一个洞。这n根绳子有一个公共的绳结x。吊好gty后蒟蒻们发现由于每个gty重力不同,绳结x在移动。蒟蒻wangxz脑洞大开的决定计算出x最后停留处的坐标,由于他太弱了决定向你求助。
不计摩擦,不计能量损失,由于gty足够矮所以不会掉到地上。

Input

输入第一行为一个正整数n(1<=n<=10000),表示gty的数目。
接下来n行,每行三个整数xi,yi,wi,表示第i个gty的横坐标,纵坐标和重力。
对于20%的数据,gty排列成一条直线。
对于50%的数据,1<=n<=1000。
对于100%的数据,1<=n<=10000,-100000<=xi,yi<=100000

Output

输出1行两个浮点数(保留到小数点后3位),表示最终x的横、纵坐标。

Sample Input

3
0 0 1
0 2 1
1 1 1

Sample Output

0.577 1.000


看了几篇讲模拟退火的博客or论文,感觉是个很高端的随机方法

(有一篇很好的博文)http://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html

像这道题:题目的大概意思就是给n个点,求出这n个点的重心坐标

其中重心满足:∑(重心与质点i的距离*质点i的质量)最小

步骤:

一开始先大致估计一下重心的位置(x, y)为所有质点横纵坐标的平均值

然后不停随机,每次在离(x, y)不超过T的范围内找一点(x', y')判断是否更可能是重心(①的值更小)

如果是,则采用新的点作为当前重心,其中每随机完一次后都会减小T的值,当T小于一个定值时结束并输出当前重心

但这可能困于局部最优解,因此模拟退火比爬山算法多了一步:不是每次都一定取最优的那一点,而是一定概率无脑选择新的点,并且这个概率随着L越来越小也会越来越小,这样就可能跳出当前的局部最优


#include
#include
#include
using namespace std;
typedef struct Point
{
	double x, y;
	double val;
}Point;
Point s[10010], ans;
int n;
double bet;
double dis(Point x, Point y)
{
	return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
double Judge(Point p)
{
	int i;
	double re=0;
	for(i=1;i<=n;i++)
		re += s[i].val*dis(p, s[i]);
	if(re0.001)
	{
		Point temp;
		temp.x = now.x+T*(Rand()*2-1);
		temp.y = now.y+T*(Rand()*2-1);
		dx = Judge(now) - Judge(temp) ;
		if(dx>0 || exp(dx/T)>Rand())
			now = temp;
		T *= 0.993;
	}
	for(i=1;i<=1000;i++)
	{
		Point temp;
		temp.x = ans.x+T*(Rand()*2-1);
		temp.y = ans.y+T*(Rand()*2-1);
		Judge(temp);
	}
}
int main(void)
{
	int i;
	while(scanf("%d", &n)!=EOF)
	{
		bet =  28104537678566885ll;
		for(i=1;i<=n;i++)
		{
			scanf("%lf%lf%lf", &s[i].x, &s[i].y, &s[i].val);
			ans.x += s[i].x;
			ans.y += s[i].y;
		}
		ans.x /= n;
		ans.y /= n;
		SA(1000000);
		printf("%.3lf %.3lf\n", ans.x, ans.y);
	}
	return 0;
}




你可能感兴趣的:(随机)