• [BZOJ 3680] 吊打XXX 【模拟退火】


    题目链接:BZOJ - 3680

    题目分析

    这道题是SLYZ的神犇把JSOI的平衡点那道题改了一下题面变成了吊打GTY神犇..Orz

    第一次写模拟退火,只能照着别人的代码写,我看的是PoPoQQQ神犇的代码,于是我基本上完全照着写的,代码没什么区别= =

    首先是模型的转化,一看这道题目是神奇的物理题,我完全就不会啊。

    搜题解,题解是这样的:根据“一切自然变化进行的方向都是使能量降低,因为能量较低的状态比较稳定”的基本物理原理,这个绳结的移动趋势是使整个系统的总能量降低。

    这个系统是一个机械系统,每个状态可以看做只具有重力势能,那么为了使所有中重物重力势能的和最小,我们就要使 sigma(d[i] * w[i]) 最大。

    其中 d[i] 是绳结到重物 i 的距离,w[i] 是重物 i 的重力。

    这样,就是求一个点,到一些顶点的加权距离和最小,是一个广义费马点问题。

    可以使用模拟退火算法来求解。

    模拟退火算法的步骤:

    选定一个初始状态(比如选定所有点坐标的平均数),选定一个初始温度 T 。

    当温度大于一个边界值时:

    {

      随机变化坐标,变化幅度为 T 。

      计算新解与当前解的差 DE。

      如果新解比当前解优(DE > 0),就用新解替换当前解。

      否则以 exp(DE / T) 的概率用新解替换当前解。

      温度乘上一个小于1的系数,即降温。
    }

    这样,随着温度不断降低,变化幅度也越来越小,接受一个更劣的解的概率也越来越小。

    最终求出一个坐标。

    为了使答案尽量优,再以这个求出的坐标为基准向四周随机移动若干次,取最优解。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    typedef double DB;
    
    const int MaxN = 10000 + 5;
    
    int n;
    
    DB Ansx, Ansy, Dis;
    DB X[MaxN], Y[MaxN], W[MaxN];
    
    inline DB Sqr(DB x) {return x * x;}
    
    inline DB d(DB x, DB y, DB xx, DB yy) 
    {
    	return sqrt(Sqr(x - xx) + Sqr(y - yy));
    }
    
    DB Calc(DB x, DB y) 
    {
    	DB ret = 0;
    	for (int i = 1; i <= n; ++i)
    		ret += W[i] * d(x, y, X[i], Y[i]);
    	if (ret < Dis)
    	{
    		Dis = ret;
    		Ansx = x;
    		Ansy = y;
    	}
    	return ret;
    }
    
    inline DB Rand() 
    {
    	return (DB)(rand() % 10000) / 10000.0;
    }
    
    void SA() 
    {
    	DB DE, T = 100000;
    	DB Nowx, Nowy, Nx, Ny;
    	Nowx = Ansx; Nowy = Ansy;
    	while (T > 0.001)
    	{
    		Nx = Nowx + T * (Rand() * 2 - 1);
    		Ny = Nowy + T * (Rand() * 2 - 1);
    		DE = Calc(Nowx, Nowy) - Calc(Nx, Ny);
    		if (DE > 0 || exp(DE / T) > Rand())
    		{
    			Nowx = Nx;
    			Nowy = Ny;
    		}
    		T *= 0.97;
    	}
    	for (int i = 1; i <= 1000; ++i)
    	{
    		Nx = Ansx + T * (Rand() * 2 - 1);
    		Ny = Ansy + T * (Rand() * 2 - 1);
    		Calc(Nx, Ny);
    	}
    }
    
    int main() 
    {
    	srand(804589);
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i)
    	{
    		scanf("%lf%lf%lf", &X[i], &Y[i], &W[i]);
    		Ansx += X[i];
    		Ansy += Y[i];
    	}
    	Ansx /= (DB)n;
    	Ansy /= (DB)n;
    	Dis = Calc(Ansx, Ansy);
    	SA();
    	printf("%.3lf %.3lf
    ", Ansx, Ansy);
    	return 0;
    }
    

      

  • 相关阅读:
    坑爹啊 StringDictionary 居然是不区分大小写的
    .NET平台OLEDB类型映射到Access数据类型 (转)
    国内省选乱做
    计算几何做题记录
    P6634 [ZJOI2020] 密码 解题报告
    AT2704 [AGC019E] Shuffle and Swap 解题报告
    ARC110F Esoswap 解题报告
    P6631 [ZJOI2020] 序列 解题报告
    P6633 [ZJOI2020] 抽卡 解题报告
    CF1605F PalindORme 解题报告
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4341029.html
Copyright © 2020-2023  润新知