Luogu P1337 [JSOI2004]平衡點 / 吊打XXX

一道入門模擬退火的經典題,仍是很考驗RP的git

首先咱們發現神TM這道題又和物理扯上了關係,實際上是一道求廣義費馬點的題目spa

首先咱們能夠根據物理知識獲得,當系統處於平衡狀態時,系統的總能量最小code

又此時系統的總能量是等於各個物體的重力勢能,在質量必定時,即要求物體離地最近,離桌子最遠blog

那麼,也就是繩子在桌子上的距離儘可能的小,即要求\(\sum_{i=1}^n m_i\cdot dist_{i,x}\)最小get

(以上物理部分推導摘於pym‘s blogit

而後考慮退火,咱們先選取一個初始位置(通常取全部點座標的平均數方便收斂)io

而後每次退火時給座標隨機一個增量(要隨溫度下降而減小,並注意須要取的),並計算新的解的答案入門

同時按照通常的模擬退火流程考慮是否接受解而且降溫便可class

PS:本題極大的考驗調參能力,本人Luogu因爲機子快,交了幾發就A了。TM的BZOJ老爺機一直在WA和TLE直接徘徊,最後好像9900+MSA了(時限10S)。im

CODE

#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<ctime>
using namespace std;
typedef double DB;
const int N=1005;
const DB EPS=1e-30,dlt=0.981;
struct data
{
    int x,y,w;
}a[N];
int n; DB ans_x,ans_y,ave_x,ave_y,ans;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag; 
}
inline DB calc(DB x,DB y)
{
    register int i; DB tot=0;
    for (i=1;i<=n;++i)
    tot+=(DB)sqrt((x-a[i].x)*(x-a[i].x)+(y-a[i].y)*(y-a[i].y))*a[i].w;
    return tot;
}
inline void Simulate_Anneal(DB x,DB y)
{
    DB T=500,res=calc(x,y); 
    for (;T>EPS;T*=dlt)
    {
        DB xx=x+(rand()*2-RAND_MAX)*T,yy=y+(rand()*2-RAND_MAX)*T,now=calc(xx,yy);
        if (now<ans) ans=now,ans_x=xx,ans_y=yy;
        if (now<res||exp((res-now)/T)>(DB)rand()/RAND_MAX) res=now,x=xx,y=yy;
    }
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i,t=50; srand(time(0)); read(n);
    for (i=1;i<=n;++i)
    {
        read(a[i].x); read(a[i].y); read(a[i].w);
        ave_x+=a[i].x; ave_y+=a[i].y;
    }
    ans_x=ave_x=(DB)ave_x/n; ans_y=ave_y=(DB)ave_y/n; ans=calc(ans_x,ans_y);
    while (t--) Simulate_Anneal(ave_x,ave_y);
    return printf("%.3lf %.3lf",ans_x,ans_y),0;
}
相關文章
相關標籤/搜索