Description
gty又虐了一场比赛,被虐的蒟蒻们决定吊打gty。gty见大势不好机智的分出了n个分身,但还是被人多势众的蒟蒻抓住了。蒟蒻们将
n个gty吊在n根绳子上,每根绳子穿过天台的一个洞。这n根绳子有一个公共的绳结x。吊好gty后蒟蒻们发现由于每个gty重力不同,绳
结x在移动。蒟蒻wangxz脑洞大开的决定计算出x最后停留处的坐标,由于他太弱了决定向你求助。
不计摩擦,不计能量损失,由于gty足够矮所以不会掉到地上。
输入第一行为一个正整数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
Solution
根据所剩无几的文化课知识可知稳定状态的总能量最小,也就是重力*距离之和最小,这就是带权费马点的模型了
模拟退火:
现在有一只wife站在绵延不绝的山脉中,他的目标是爬上最高的山。由于wife比较naive他只会看看附近的山然后爬上能看见的最高的那座山
显然这样局部最优解不一定是全局最优解,可以考虑设置一个温度t,每次随机地踏出一步,如果是更优那么就走,如果不是更优则比较
e△V△T
e
△
V
△
T
和一个[0,1]之间的随机实数,如果函数值大就走这一步。做完了就降温
大概就是这样,可以理解为随机化的贪心
随机种子的设定可以是生日^名字首字母的ascii对长者生日取模,这样看起来比较随机
Code
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
const int N=200005;
struct pos {
double x,y,w;
pos operator +(pos b) {
return (pos) {x+b.x,y+b.y,w+b.w};
}
} now,prt,p[N];
double ans=10000.0;
int n;
double get() {
return (double)(rand()%10000)/10000.0;
}
double get_dis(pos x,pos y) {
return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
double calc(pos x) {
double ret=0;
rep(i,1,n) ret+=p[i].w*get_dis(x,p[i]);
if (ret<ans) {
ans=ret;
prt=x;
}
return ret;
}
void solve() {
double t=100000.0; now=prt;
double rec=calc(now);
while (t>0.001) {
pos tar=now+(pos) {t*(get()*2-1),t*(get()*2-1),0};
double tmp=calc(tar);
double delta=rec-tmp;
if (delta>0||exp(delta/t)>get()) {
now=tar;
rec=tmp;
}
t*=0.98;
}
rep(i,1,1000) {
pos tar=prt+(pos) {t*(get()*2-1),t*(get()*2-1),0};
calc(tar);
}
}
int main(void) {
srand(15727334);
scanf("%d",&n);
rep(i,1,n) {
scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].w);
prt=prt+p[i];
}
prt.x/=1.0*n; prt.y/=1.0*n; ans=calc(prt);
solve();
printf("%.3lf %.3lf\n", prt.x,prt.y);
return 0;
}