题解:P5544 [JSOI2016] 炸弹攻击1

这题很明显是一道模拟退火,直接按照模板写出代码来:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 10 + 5;
const int M = 1e3 + 5;
const double down = 0.9985;
int n, m, R, ans;
struct peo{
	double x, y;
}b[M];
struct bui{
	double x, y, r;
}a[N];
int bst;
double tx, ty;
double dist(double x1, double y1, double x2, double y2) {
	return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
int energy(double x, double y) {
	double r = R;
	for (int i = 1; i <= n; i++) {
		r = min(r, dist(x, y, a[i].x, a[i].y) - a[i].r);
	}
	int tot = 0;
	for (int i = 1; i <= m; i++) {
		if(dist(x, y, b[i].x, b[i].y) <= r) {
			tot++;
		}
	}
	return tot;
}
void SA() {
	double t = 1e4;
	while (t > 1e-10) {
		double dx = tx + (rand() * 2 - RAND_MAX) * t;
		double dy = ty + (rand() * 2 - RAND_MAX) * t;
		int now = energy(dx, dy);
		double delta = bst - now;
		if (delta < 0) {
			tx = dx;
			ty = dy;
			bst = now;
            ans =max(ans, now);
		}
		else if (exp(delta / t) * RAND_MAX < rand()) {
			tx = dx;
			ty = dy;
			bst = now;
		}
		t *= down;
	}
}
signed main() {
    srand(324531);
	cin >> n >> m >> R;
	for (int i = 1; i <= n; i++) {
		cin >> a[i].x >> a[i].y >> a[i].r;
	}
	for (int i = 1; i <= m; i++) {
		cin >> b[i].x >> b[i].y;
		tx += b[i].x;
		ty += b[i].y;
	}
	tx /= m;
	ty /= m;
    bst = energy(tx, ty);
    while (((double)clock() / CLOCKS_PER_SEC) < 0.9)
	SA();
	cout << ans;
	return 0;
}

如果这样子,你可以过掉这道题的原始数据,但是,由于用户 do_it_tomorrow 坚持不懈的 HACK,我们会 WA 在 #12 上。

我们开始乱搞:

首先,我们可以先以 $\left { \frac{\sum_{i=1}^{m} p_i}{m} ,\frac{\sum_{i=1}^{m} q_i}{m} \right } $ 为初始点位进行一次模拟退火,然后分别将每一个敌人设为初始点位进行一次模拟退火,这样会让答案越来越接近最优解。但是如果只这样做了话,会 TLE 一堆点,得不偿失。

我们随便动一下参数,再进行去重,然后让设置每一个敌人为初始点位进行一次模拟退火的概率为 \(\frac{3}{4}\)。这样就可以过 HACK 了。

AC 代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 10 + 5;
const int M = 1e3 + 5;
const double down = 0.999;
int n, m, R, ans;
struct peo{
	double x, y;
}b[M];
struct bui{
	double x, y, r;
}a[N];
int bst;
double tx, ty;
double dist(double x1, double y1, double x2, double y2) {
	return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
int energy(double x, double y) {
	double r = R;
	for (int i = 1; i <= n; i++) {
		r = min(r, dist(x, y, a[i].x, a[i].y) - a[i].r);
	}
	int tot = 0;
	for (int i = 1; i <= m; i++) {
		if(dist(x, y, b[i].x, b[i].y) <= r) {
			tot++;
		}
	}
	return tot;
}
void SA() {
	double t = 2000;
	while (t > 1e-10) {
		double dx = tx + (rand() * 2 - RAND_MAX) * t;
		double dy = ty + (rand() * 2 - RAND_MAX) * t;
		int now = energy(dx, dy);
		double delta = bst - now;
		if (delta < 0) {
			tx = dx;
			ty = dy;
			bst = now;
			ans =max(ans, now);
		}
		else if (exp(delta / t) * RAND_MAX < rand()) {
			tx = dx;
			ty = dy;
			bst = now;
		}
		t *= down;
	}
}
map<pair<int, int>, bool> vis;
signed main() {
	srand(132052);
	cin >> n >> m >> R;
	for (int i = 1; i <= n; i++) {
		cin >> a[i].x >> a[i].y >> a[i].r;
	}
	for (int i = 1; i <= m; i++) {
		cin >> b[i].x >> b[i].y;
		tx += b[i].x;
		ty += b[i].y;
	}
	tx /= m;
	ty /= m;
	bst = max(bst, energy(tx, ty));
	SA();
	for (int i = 1; i <= m; i++) {
		tx = b[i].x;
		ty = b[i].y;
		if (((double)clock() / CLOCKS_PER_SEC) > 0.9) break;
		if(!vis[{tx, ty}] && rand() % 4 != 0) {
			SA();
			vis[{tx, ty}] = true;
		}
		
	}
		
	cout << ans;
	return 0;
}
posted @ 2026-02-24 17:31  Python_enjoy  阅读(1)  评论(0)    收藏  举报