bzoj3680 吊打XXX 模拟退火

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,每次随机地踏出一步,如果是更优那么就走,如果不是更优则比较 eVT 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值