bzoj 3680: 吊打XXX 模拟退火
题目大意:
给定n个点及每个点的附加权值,求一个点使其到这n个点的加权距离和最小.
(\(n \leq 100000\))
题解:
同poj 2420
把将状态映射到实数的函数改一改就行了.
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 10010;
const double eps = 1e-3;
const double det = 0.99;
const double pi = acos(-1.0);
struct Point{
double x,y;
Point(const double&a=0,const double &b=0){x=a;y=b;}
};
inline double sqr(const double &x){return x*x;}
inline double dis(const Point &a,const Point &b){
return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
}
Point p[maxn],nw,ans;
double ans_min = 1e100;
int n,w[maxn];
double f(const Point &x){
double ret = 0;
for(int i=1;i<=n;++i) ret += dis(x,p[i])*w[i];
if(ret < ans_min) ans_min = ret,ans = x;
return ret;
}
inline double ran(){
return (double)(rand() % 1000 + 1)/1000.0;
}
int main(){
srand(2333);
read(n);
for(int i=1,x;i<=n;++i){
read(x);nw.x+=x;p[i].x = x;
read(x);nw.y+=x;p[i].y = x;
read(w[i]);
}nw.x /= 1.0*n;nw.y /= 1.0*n;
double T = 1000.0,x,de;
bool flag = false;
while(T > eps){
flag = true;
while(flag){
flag = false;
for(int i=1;i<=4;++i){
x = pi*2.0*ran();
Point nx(nw.x+T*cos(x),nw.y+T*sin(x));
de = f(nw) - f(nx);
if(de > 0 || exp(de/T) > ran()){
nw = nx;flag = true;
}
T *= det;
}
}
}
T = eps;
for(int i=1;i<=1000;++i){
x = pi*2.0*ran();
f(Point(ans.x+T*cos(x)*ran(),ans.y+T*sin(x)*ran()));
}
printf("%.3lf %.3lf\n",ans.x,ans.y);
getchar();getchar();
return 0;
}
人就像命运下的蝼蚁,谁也无法操控自己的人生.