[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;
}

猜你喜欢

转载自www.cnblogs.com/skylee03/p/9722560.html