「JSOI2016」炸弹攻击(模拟退火)
模拟退火居然不出题答,这个出题人脑子进水了。
由最小圆覆盖那一套,最优的圆是以上三种情况之一:
1.一个答案点就是圆,半径—>0
2.两个点连成的线段为直径的圆
3.三点共园
直接枚举,判断是\(O(m^4)\)的。
可能可以优化掉一个\(m\),然后你发现做不下去了。
考虑退火,这个题直接以覆盖的点数作为价值退火是不太行的。
当价值相同时,应取当前圆的最大半径作为第二关键字。
然后调整发现,应该使往劣的方向走的概率变小。
这么个连样例都吃力的做法它能过。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
#define db double
const int N = 1005;
int n, m;
db R, r[N];
struct P {
db x, y;
P(db _x = 0, db _y = 0) {
x = _x, y = _y;
}
} a[N], b[N];
#define sqr(x) ((x) * (x))
db dis(P a, P b) {
return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
}
ll rand(ll x, ll y) {
return ((ll) RAND_MAX * rand() + rand()) % (y - x + 1) + x;
}
db rd() {
return (db) rand(0, 1e8) / 1e8;
}
const db eps = 1e-8;
P calc(P c) {
db mi = R;
fo(i, 1, n) mi = min(mi, dis(a[i], c) - r[i]);
int s = 0;
fo(i, 1, m) s += dis(c, b[i]) <= mi;
return P(s, mi);
}
int main() {
srand(time(0) + clock());
scanf("%d %d %lf", &n, &m, &R);
fo(i, 1, n) scanf("%lf %lf %lf", &a[i].x, &a[i].y, &r[i]);
fo(i, 1, m) scanf("%lf %lf", &b[i].x, &b[i].y);
int ans = 0;
fo(ii, 1, min(50000 / m, 1000)) {
P c = b[rand(1, m)];
for(db T = 100; T > eps; T *= 0.99) {
P d = c;
d.x += (rd() - 0.5) * T * (R / 200), d.y += (rd() - 0.5) * T * (R / 200);
P v1 = calc(c), v2 = calc(d);
ans = max(ans, (int) v2.x);
if(v2.x > v1.x) {
c = d;
continue;
}
if(v1.x == v2.x) {
if(v2.y > v1.y) {
c = d;
continue;
}
if(exp((db) (v2.y - v1.y) / T) / 10 * 1e9 > rand(1, 1e9)) {
c = d;
}
continue;
}
if(exp((db) (v2.x - v1.x) / T) / 10 * 1e9 > rand(1, 1e9)) {
c = d;
}
}
}
pp("%d\n", ans);
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址