JSOI2004平衡点

题目链接:https://www.luogu.org/problemnew/show/P1337#sub

可以用模拟退火来解这道题,关于模拟退火可以先看这几篇博客:

https://www.cnblogs.com/peng-ym/p/9158909.html

https://blog.csdn.net/numberer/article/details/79996753

https://blog.csdn.net/sci_m3/article/details/51539003

第一部分:首先要知道的是系统平衡时总能量最低,这里就是重力势能最低,也就是绳子在桌面上的长度最短,这里有一个重力势能的小转化,可以自己推一下(绳长一定,桌面高度一定),就是我们可以拿在桌面上的绳子的长度乘以物体的重力来等效物体的重力势能。

第二部分:也就是模拟退火部分,这里的难点主要是参数的选择,主要是靠经验来选择的,当然也可以借鉴一下网上dalao们的博客。具体看代码吧。

code:

#include
#include
#include
#include
#include
using namespace std;

const int N=1e3+10;
const double EPS=1e-6;
struct node {
	int x, y, w;
} p[N];
int n;
double ansx, ansy;

double f(double x, double y) {
	int i;
	double tot=0;
	for (i=1; i<=n; i++)
		tot+=sqrt((p[i].x-x)*(p[i].x-x)+(p[i].y-y)*(p[i].y-y))*p[i].w;
	return tot;
}

//r和eps都是很重要的参数,r越趋近于1,eps精度越高,那么程序的正确性就越高,但是时间复杂度会越高
void work() {
	double t=200, r=0.998;
	while (t>EPS) {
		double nowx=ansx+(rand()*2-RAND_MAX)*t, nowy=ansy+(rand()*2-RAND_MAX)*t, de=f(nowx, nowy)-f(ansx, ansy);
		if (de<0) ansx=nowx, ansy=nowy;
		else if (exp(-de/t)*RAND_MAX>rand()) ansx=nowx, ansy=nowy;//降温de必须要小于0,因此必要时需要加上负号
		t*=r;
	}
	return ;
}

int main() {
	srand(time(0));
	int i;
	cin >> n;
	for (i=1; i<=n; i++) {
		cin >> p[i].x >> p[i].y >> p[i].w;
		ansx+=p[i].x, ansy+=p[i].y;  
	}
	ansx/=n, ansy/=n;
	work();
	printf("%.3lf %.3lf", ansx, ansy);
	return 0;
}

模拟退火是随机算法,不一定能够一次A掉,但可以用来骗分。

你可能感兴趣的:(算法竞赛)