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;
}
posted @ 2017-02-24 13:49  Sky_miner  阅读(208)  评论(0编辑  收藏  举报