[JSOI2004]平衡点/[BZOJ3680]吊打XXX

[JSOI2004]平衡点/[BZOJ3680]吊打XXX

题目大意:

\(n(n\le10000)\)个重物,每个重物系在一条足够长的绳子上。每条绳子自上而下穿过桌面上的洞,然后系在一起。假设绳子是完全弹性的,桌子足够高,且忽略所有的摩擦。问绳结最终平衡于何处。

思路:

模拟退火。

源代码:

#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
inline int getint() {
	register char ch;
	register bool neg=false;
	while(!isdigit(ch=getchar())) neg|=ch=='-';
	register int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return neg?-x:x;
}
const int N=1e4+1;
int n;
double w[N];
struct Point {
	double x,y;
};
Point p[N],ans;
inline double getrd() {
	return rand()%10000/1e4;
}
inline double sqr(const double &x) {
	return x*x;
}
inline double dist(const Point &a,const Point &b) {
	return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
inline double calc(const Point &q) {
	double ret=0;
	for(register int i=1;i<=n;i++) {
		ret+=dist(p[i],q)*w[i];
	}
	return ret;
}
inline void sa(double T) {
	Point q=ans;
	for(;T>0.001;T*=0.98) {
		const Point p=(Point){q.x+(getrd()*2-1)*T,q.y+(getrd()*2-1)*T};
		const double d=calc(q)-calc(p);
		if(calc(ans)-calc(p)>0) ans=p;
		if(d>0||exp(d/T)>getrd()) q=p;
	}
	for(register int i=1;i<=1000;i++) {
		const Point p=(Point){ans.x+(getrd()*2-1)*T,ans.y+(getrd()*2-1)*T};
		if(calc(ans)-calc(p)>0) ans=p;
	}
}
int main() {
	srand(19260817);
	n=getint();
	for(register int i=1;i<=n;i++) {
		p[i].x=getint();
		p[i].y=getint();
		w[i]=getint();
		ans.x+=p[i].x;
		ans.y+=p[i].y;
	}
	ans.x/=n;
	ans.y/=n;
	sa(1e5);
	printf("%.3f %.3f\n",ans.x,ans.y);
	return 0;
}
posted @ 2018-09-29 10:57  skylee03  阅读(165)  评论(0编辑  收藏  举报