1. 程式人生 > 實用技巧 >CH6803 導彈防禦塔 (二分 + 匈牙利 / 網路流)

CH6803 導彈防禦塔 (二分 + 匈牙利 / 網路流)

連結:https://ac.nowcoder.com/acm/contest/1062/D

題目描述

Freda的城堡——
“Freda,城堡外發現了一些入侵者!”
“喵...剛剛探究完了城堡建設的方案數,我要歇一會兒嘛lala~”
“可是入侵者已經接近城堡了呀!”
“別擔心,rainbow,你看呢,這是我剛設計的導彈防禦系統的說~”

“喂...別賣萌啊……”

Freda控制著N座可以發射導彈的防禦塔。每座塔都有足夠數量的導彈,但是每座塔每次只能發射一枚。在發射導彈時,導彈需要T1秒才能從防禦塔中射出,而在發射導彈後,發射這枚導彈的防禦塔需要T2分鐘來冷卻。
所有導彈都有相同的勻速飛行速度V,並且會沿著距離最短的路徑去打擊目標。計算防禦塔到目標的距離Distance時,你只需要計算水平距離,而忽略導彈飛行的高度。導彈在空中飛行的時間就是 (Distance/V) 分鐘,導彈到達目標後可以立即將它擊毀。
現在,給出N座導彈防禦塔的座標,M個入侵者的座標,T1、T2和V,你需要求出至少要多少分鐘才能擊退所有的入侵者。

輸入描述:

第一行五個正整數N,M,T1,T2,V。
接下來M行每行兩個整數,代表入侵者的座標。
接下來N行每行兩個整數,代表防禦塔的座標。

輸出描述:

輸出一個實數,表示最少需要多少分鐘才能擊中所有的入侵者,四捨五入保留六位小數。

示例1

輸入

3 3 30 20 1
0 0
0 50
50 0
50 50
0 1000
1000 0

輸出

91.500000

最大最小,二分走起。

程式碼寫法和 關押罪犯 很像

題目要求所有目標被擊毀的時間最大值最小,所以我們可以考慮二分答案(這是一個很重要的方法)

我們考慮如何在當前二分的答案下儘量多的打擊目標

由於每個防禦塔都可以發射任意發導彈攻擊,所以我們不妨把每個防禦塔都先打出去M發導彈

把能在時間限制內打出的每發導彈都和這發導彈能在時間範圍內打到的目標連一條邊

然後題目就轉化成經典的二分圖最大匹配

用匈牙利或者網路流跑一遍就好了
匈牙利又短又快,強烈建議打匈牙利(網路流非常非常的長)

#include<bits/stdc++.h>
#define pii pair<int, int>
#define x first
#define y second
using namespace std;
const int N = 56, M = 2506;
const double eps = 1e-8;
int n, m, t, t2, V, f[M];
double t1;
bool v[M];
pii a[N], b[N];//敵人和防禦塔座標
pair<int, double> c[M];
vector<int> e[N];

inline double S(pii a, pii b) {
	int dx = a.x - b.x, dy = a.y - b.y;
	return sqrt(dx * dx + dy * dy);
}

bool dfs(int x) {
	for (unsigned int i = 0; i < e[x].size(); i++) {
		int y = e[x][i];
		if (v[y]) continue;
		v[y] = 1;
		if (!f[y] || dfs(f[y])) {
			f[y] = x;
			return 1;
		}
	}
	return 0;
}

inline bool pd(double mid) {
	memset(f, 0, sizeof(f));
	for (int i = 1; i <= m; i++) {
		e[i].clear();
		for (int j = 1; j <= t; j++)
			if (c[j].y + S(a[i], b[c[j].x]) / V <= mid)
				e[i].push_back(j);
	}
	for (int i = 1; i <= m; i++) {
		memset(v, 0, sizeof(v));
		if (!dfs(i)) return 0;
	}
	return 1;
}

int main() {
	freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m >> t1 >> t2 >> V;
	t = n * m;
	t1 /= 60;
	for (int i = 1; i <= m; i++)
		scanf("%d %d", &a[i].x, &a[i].y);
	for (int i = 1; i <= n; i++)
		scanf("%d %d", &b[i].x, &b[i].y);
	for (int i = 1; i <= m; i++)
		for (int j = 1; j <= n; j++) {
			int k = (i - 1) * n + j;
			c[k].x = j;
			c[k].y = (i - 1) * (t1 + t2) + t1;
		}
	double l = t1, r = 100000;
	while (l + eps < r) {
		double mid = (l + r) / 2;
		if (pd(mid)) r = mid;
		else l = mid;
	}
	printf("%.6f\n", l);
}