1. 程式人生 > WINDOWS開發 >「AcWing 374」「BZOJ 3035」導彈防禦塔

「AcWing 374」「BZOJ 3035」導彈防禦塔

Description

Freda 的城堡遭受了 \(M\) 個入侵者的攻擊!

Freda 控制著 \(N\) 座導彈防禦塔,每座塔都有足夠數量的導彈,但是每次只能發射一枚。

在發射導彈時,導彈需要 \(T_1\) 秒才能從防禦塔中射出,而在發射導彈後,發射這枚導彈的防禦塔需要 \(T_2\) 分鐘來冷卻。

所有導彈都有相同的勻速飛行速度 \(V\),並且會沿著距離最短的路徑去打擊目標。

計算防禦塔到目標的距離 \(\text{Distance}\) 時,你只需要計算水平距離,而忽略導彈飛行的高度。

導彈在空中飛行的時間就是 (\(\text{Distance}/V\)) 分鐘,導彈到達目標後可以立即將它擊毀。

現在,給出 \(N\) 座導彈防禦塔的座標,\(M\) 個入侵者的座標,\(T_1,T_2\)\(V\)。因為 Freda 的小夥伴 Rainbow 就要來拜訪城堡了,你需要求出至少多少分鐘才能擊退所有的入侵者。

Hint

  • \(1\le N,M\le 50\)
  • \(1\le T_1,T_2,V \le 2\times 10^3\)
  • 座標絕對值不超過 \(10^4\)

Solution

直接做不太好做,不如將問題轉化一下,判斷能否在時間 \(t\) 內擊退所有入侵者。

顯然如果在 \(t\) 時間內可以擊退,那麼 \(t + k(k>0)\) 的時間內也一定可以。滿足單調性,就可以 二分答案

了。


考慮如果一個導彈防禦塔如果只能發射一枚導彈,那就是一個 二分圖最大匹配 問題。兩邊的點分別為防禦塔和入侵者。

如果可以發射不止一個,那就是 多重匹配問題


多重匹配問題的常規解決方式之一是 拆點

具體地,就是 \(t\) 時間內,若該防禦塔可以發射 \(x\) 個導彈,那麼就拆成 \(x\) 個點,然後使每一個入侵者的點向這些點連一條邊。


對於二分中某一個 \(t\) 的可行性的判斷,只要檢查 \(m\) 個入侵者的點是否都有導彈的點與之匹配即可。

也可以判斷最大匹配對數是否等於 \(m\)


考慮到導彈拆點後點數多且不好控制,推薦從入侵者的點這一邊搜。

既減少了程式碼難度,也大大提高了程式碼執行效率(\(3266 \text{ms} \rightarrow 204 \text{ms}\)

)。

Code

#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,sizeof match);
	memset(vis,sizeof vis);
}

bool Dfs(int x,int t) {
	for (vector<int>::iterator y = G[x].begin(); y != G[x].end(); y++)
		if (vis[*y] != t) {
			vis[*y] = t;
			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\n",ub);
	return 0;
}