1. 程式人生 > 其它 >P1337 [JSOI2004]平衡點 / 吊打XXX 【模擬退火】

P1337 [JSOI2004]平衡點 / 吊打XXX 【模擬退火】

洛谷 P1337 -> Click Here

題意

\(n\) 個點在一個平面上,且第 \(i\) 個點對點 \(X\) 施一拉力 \(w_i\) ,尋找 \(X\) 的平衡位置

思路

模擬退火經典入門題目,雖然是單峰函式,但也可用退火解決

設初始點為每個點的平衡位置,進行模擬退火,尋找新點,檢視當前位置是否更優或是否在一定可接受的概率內,逐漸降溫,平衡點也逐漸精確

code

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define down 0.996
#define REP(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
struct edge{double x,y,w;} E[100005];
int n;
double ansx,ansy,answ;
double getde(double x,double y){
	double r=0,dx,dy;
	REP(i,1,n){
		dx=x-E[i].x;
		dy=y-E[i].y;
		r+=sqrt(dx*dx+dy*dy)*E[i].w;
	}
	return r;
}
void sa(){
	double t=4000;
	while(t>1e-15){
		double ex=ansx+(rand()*2-RAND_MAX)*t;
		double ey=ansy+(rand()*2-RAND_MAX)*t;
		double ew=getde(ex,ey);
		double dw=ew-answ;
		if(dw<0){
			ansx=ex;
			ansy=ey;
			answ=ew;
		}else if(exp(-dw/t)*RAND_MAX>rand()){
			ansx=ex;
			ansy=ey;
		}
		t*=down;
	}
}
int main(){
	scanf("%d",&n);
	REP(i,1,n){
		scanf("%lf%lf%lf",&E[i].x,&E[i].y,&E[i].w);
		ansx+=E[i].x;
		ansy+=E[i].y;
	}
	ansx/=n;
	ansy/=n;
	answ=getde(ansx,ansy);
    int cnt=4;while(cnt--) sa();
	printf("%.3lf %.3lf\n",ansx,ansy);
	return 0;
}