「AcWing 374」「BZOJ 3035」导弹防御塔

Description

Freda 的城堡遭受了 (M) 个入侵者的攻击!

Freda 控制着 (N) 座导弹防御塔,每座塔都有足够数量的导弹,但是每次只能发射一枚。

在发射导弹时,导弹需要 (T_1) 秒才能从防御塔中射出,而在发射导弹后,发射这枚导弹的防御塔需要 (T_2) 分钟来冷却。

所有导弹都有相同的匀速飞行速度 (V),并且会沿着距离最短的路径去打击目标。

计算防御塔到目标的距离 ( ext{Distance}) 时,你只需要计算水平距离,而忽略导弹飞行的高度。

导弹在空中飞行的时间就是 (( ext{Distance}/V)) 分钟,导弹到达目标后可以立即将它击毁。

现在,给出 (N) 座导弹防御塔的坐标,(M) 个入侵者的坐标,(T_1,T_2)(V)。因为 Freda 的小伙伴 Rainbow 就要来拜访城堡了,你需要求出至少多少分钟才能击退所有的入侵者。

Hint

  • (1le N, Mle 50)
  • (1le T_1, T_2, V le 2 imes 10^3)
  • 坐标绝对值不超过 (10^4)

Solution

直接做不太好做,不如将问题转化一下,判断能否在时间 (t) 内击退所有入侵者。

显然如果在 (t) 时间内可以击退,那么 (t + k(k>0)) 的时间内也一定可以。满足单调性,就可以 二分答案 了。


考虑如果一个导弹防御塔如果只能发射一枚导弹,那就是一个 二分图最大匹配 问题。两边的点分别为防御塔和入侵者。

如果可以发射不止一个,那就是 多重匹配问题


多重匹配问题的常规解决方式之一是 拆点

具体地,就是 (t) 时间内,若该防御塔可以发射 (x) 个导弹,那么就拆成 (x) 个点,然后使每一个入侵者的点向这些点连一条边。


对于二分中某一个 (t) 的可行性的判断,只要检查 (m) 个入侵者的点是否都有导弹的点与之匹配即可。

也可以判断最大匹配对数是否等于 (m)


考虑到导弹拆点后点数多且不好控制,推荐从入侵者的点这一边搜。

既减少了代码难度,也大大提高了代码运行效率((3266 ext{ms} ightarrow 204 ext{ms}))。

匈牙利算法,时间复杂度:(O(n^2m log t))

Code

/*
 * Author : _Wallace_
 * Source : https://www.cnblogs.com/-Wallace-/
 * Problem : AcWing 374 BZOJ 3035 导弹防御塔
 */
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;
const int N = 55;
const int S = 1e4 + 5;
const double eps = 1e-8;

int mis[2][N], inv[2][N];
int n, m, total;
double t1, t2, v;

double cost[N][N];
pair<int, double> extra[S];
#define sqr(x) ((x) * 1.0 * (x))
inline double dist(int x1, int y1, int x2, int y2) {
	return sqrt(sqr(x1 - x2) + sqr(y1 - y2));
}

vector<int> G[N];
int match[S], vis[S];

void resetData() {
	for (register int i = 1; i <= m; i++)
		G[i].clear();
	memset(match, 0, sizeof match);
	memset(vis, 0, sizeof vis);
}

bool Dfs(int x, int t) {
	if (vis[x] == t) return false;
	vis[x] = t;
	for (vector<int>::iterator y = G[x].begin(); y != G[x].end(); y++)
		if(!match[*y] || Dfs(match[*y], t))
			return match[*y] = x, true;
	return false;
}

bool judge(double t) {
	resetData();
	for (register int i = 1; i <= m; i++)
		for (register int j = 1; j <= total; j++)
			if (extra[j].second + cost[extra[j].first][i] <= t)
				G[i].push_back(j);
	
	for (register int i = 1; i <= m; i++)
		if (!Dfs(i, i)) return false;
	return true;
}

void calcCost() {
	for (register int i = 1; i <= n; i++)
		for (register int j = 1; j <= m; j++)
			cost[i][j] = dist(mis[0][i], mis[1][i], inv[0][j], inv[1][j]) / v;
	
	total = n * m;
	for (register int i = 1; i <= n; i++)
		for (register int j = 1; j <= m; j++) {
			int pos = (i - 1) * m + j;
			extra[pos] = make_pair(i, t1 + (t1 + t2) * (j - 1));
		}
}

signed main() {
	scanf("%d%d%lf%lf%lf", &n, &m, &t1, &t2, &v);
	t1 /= 60;
	for (register int i = 1; i <= m; i++)
		scanf("%d%d", &inv[0][i], &inv[1][i]);
	for (register int i = 1; i <= n; i++)
		scanf("%d%d", &mis[0][i], &mis[1][i]);
	
	calcCost();
	
	double lb = t1, ub = 1e6;
	while (ub - lb > eps) {
		double mid = (lb + ub) / 2;
		if (judge(mid)) ub = mid;
		else lb = mid;
	}
	printf("%.6f
", ub);
	return 0;
}
原文地址:https://www.cnblogs.com/-Wallace-/p/12826902.html