ch_g

ECUST_ACMer —— ch_g
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[URAL][1520][Empire Strikes Back][模拟退火]

Posted on 2011-03-08 12:30  ch_g  阅读(348)  评论(0编辑  收藏  举报
/*
题目:Empire Strikes Back
题目来源:URAL 1520
题目难度:中等偏难
题目内容或思路:
模拟退火
08年顾研论文例题。一开始没看论文只用模拟退火一直WA,后来看了
论文才发现,如果所找的点在圆的边上的话,很难调整到该点。所以
还要加上O(n^3)的特判
做题日期:2011.3.8
*/
#include
<cstdio>
#include
<cstdlib>
#include
<climits>
#include
<iostream>
#include
<algorithm>
#include
<cstring>
#include
<string>
#include
<queue>
#include
<map>
#include
<vector>
#include
<bitset>
#include
<cmath>
#include
<set>
#include
<utility>
#include
<ctime>
#define sqr(x) ((x)*(x))
using namespace std;

const int N = 310, g = 20, L = 30;
const double pi = acos(-1.0);
const double inf = 1e10;
const double eps = 1e-9;
int n;
double r, delta;
struct cpoint {
double x, y, d;
}cp[N], test[N], o;

int dcmp(double x) {
if (x < -eps) return -1; else return x > eps;
}

double dissqr(cpoint a, cpoint b) {
return sqr(a.x - b.x) + sqr(a.y - b.y);
}

void calc(cpoint &p) {
p.d
= inf;
if (dcmp(sqr(p.x) + sqr(p.y) - r * r) > 0) {
p.d
= 0; return;
}
for (int i = 0; i < n; ++i) {
p.d
= min(p.d, dissqr(p, cp[i]));
}
}

void updata(int id) {
cpoint t;
double angle = rand();
angle
-= (int)(angle / pi / 2) * pi * 2;
t.x
= test[id].x + delta * cos(angle);
t.y
= test[id].y + delta * sin(angle);
calc(t);
if (t.d > test[id].d) test[id] = t;
}

int CirCrossLine(cpoint p0, double r,
double a, double b, double c, cpoint &cp1, cpoint &cp2) {
double aa = a * a, bb = b * b, s = aa + bb;
double d = r * r * s - sqr(a * p0.x + b * p0.y + c);
if (d + eps < 0) return 0;
if (d < eps) d = 0; else d = sqrt(d);
double ab = a * b, bd = b * d, ad = a * d;
double xx = bb * p0.x - ab * p0.y - a * c;
double yy = aa * p0.y - ab * p0.x - b * c;
cp2.x
= (xx + bd) / s; cp2.y = (yy - ad) / s;
cp1.x
= (xx - bd) / s; cp1.y = (yy + ad) / s;
if (d > eps) return 2; else return 1;
}

void solve() {
// srand(time(0));
for (int i = 0; i < n; ++i) {
scanf(
"%lf%lf", &cp[i].x, &cp[i].y);
}
for (int i = 0; i < g; ++i) {
double len = rand();
len
-= (int)(len / r) * r;
double angle = rand();
angle
-= (int)(angle / pi / 2) * pi * 2;
test[i].x
= len * cos(angle);
test[i].y
= len * sin(angle);
calc(test[i]);
}
delta
= r * 2 / sqrt((double)g);
for (; delta > 1e-7; delta *= 0.8)
for (int i = 0; i < g; ++i)
for (int j = 0; j < L; ++j)
updata(i);
double res = 0;
for (int i = 0; i < g; ++i)
res
= max(res, test[i].d);
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
cpoint p1, p2;
CirCrossLine(o, r,
2 * (cp[i].x - cp[j].x), 2 * (cp[i].y - cp[j].y),
dissqr(cp[j], o)
- dissqr(cp[i], o), p1, p2);
calc(p1);
res
= max(res, p1.d);
calc(p2);
res
= max(res, p2.d);
}
}
for (int i = 0; i < n; ++i) {
double d = sqrt(dissqr(cp[i], o));
if (dcmp(d) <= 0) continue;
cpoint p;
p.x
= cp[i].x * r / d;
p.y
= cp[i].y * r / d;
calc(p);
res
= max(res, p.d);
p.x
= -p.x; p.y = -p.y;
calc(p);
res
= max(res, p.d);
}
printf(
"%lf\n", sqrt(res));
}

int main() {
#ifndef ONLINE_JUDGE
freopen(
"D:\\in.txt", "r", stdin);
#endif
o.x
= o.y = 0;
while (scanf("%d%lf", &n, &r) != EOF) {
solve();
}
return 0;
}