[HDU]2295 Radar 跳舞链 重复覆盖

Radar




 

Problem Description

 

N cities of the Java Kingdom need to be covered by radars for being in a state of war. Since the kingdom has M radar stations but only K operators, we can at most operate K radars. All radars have the same circular coverage with a radius of R. Our goal is to minimize R while covering the entire city with no more than K radars.
 


 

Input

 

The input consists of several test cases. The first line of the input consists of an integer T, indicating the number of test cases. The first line of each test case consists of 3 integers: N, M, K, representing the number of cities, the number of radar stations and the number of operators. Each of the following N lines consists of the coordinate of a city.
Each of the last M lines consists of the coordinate of a radar station.

All coordinates are separated by one space.
Technical Specification

1. 1 ≤ T ≤ 20
2. 1 ≤ N, M ≤ 50
3. 1 ≤ K ≤ M
4. 0 ≤ X, Y ≤ 1000
 


 

Output

 

For each test case, output the radius on a single line, rounded to six fractional digits.
 


 

Sample Input

 

  
  
1 3 3 2 3 4 3 1 5 4 1 1 2 2 3 3
 


 

Sample Output

 

  
  
2.236068
 


 

Source

 

The 4th Baidu Cup final


  这道题的大意就是说有m个监控范围一样的雷达, 监控范围为圆形. 现在给出n个城市的二维坐标, 问能否用<=K个雷达覆盖所有城市, 若果能请求出能覆盖的最小的半径.

  又是覆盖问题... 那么要在满足限制的条件下得出最小解, 那一般都是二分答案... 考虑怎么check. 如果二分的是半径, 那么每个雷达能覆盖哪些城市就能够确定... 再看一下数据范围... 搜索可搞. 又要求完全覆盖, 自然想到跳舞链. 雷达为行, 城市为列, 这样就转化模型了. 然后就是裸的重复覆盖.(没要求一个城市只能被一个雷达覆盖).

  对于重复覆盖问题, 与精确覆盖的区别就在于删的方式. 精确覆盖删行, 重复覆盖删列. 比如说选择了c列, 那么就把c列删去, 然后对于选择的某行i, i覆盖了哪些列就把哪些列删去. 之所以不能删行就是因为可以重复覆盖. 但是这样删的力度不大, 就会导致了图会很慢才能变得稀疏. 所以要像A*那样写一个最乐观的估价函数. 如果这个函数返回的值加上当前已经删去的行>K, 那么显然不必要再往更深里搜索了.

#include<bits/stdc++.h>
using namespace std;
const int maxm = 55;
const int maxn = 3e3;
const double eps = 1e-8;
bool vis[maxm];
int n, m, siz, N, M, K, T;
int s[maxm], h[maxm];
int le[maxn], ri[maxn], up[maxn], dw[maxn], col[maxn];
inline void init(const int &r, const int &c) {
	n = r, m = c, siz = m;
	for (int i = 0; i <= m; ++ i) {
		s[i] = 0;
		le[i] = i - 1;
		ri[i] = i + 1;
		up[i] = dw[i] = i;
	}
	ri[m] = 0, le[0] = m;
	for (int i = 1; i <= n; ++ i) h[i] = -1;
}
inline void link(const int &r, const int &c) {
	s[col[++ siz] = c] ++;
	dw[siz] = dw[c], up[dw[c]] = siz;
	dw[c] = siz, up[siz] = c;
	if (h[r] < 0) h[r] = le[siz] = ri[siz] = siz;
	else {
		ri[siz] = ri[h[r]], le[ri[h[r]]] = siz;
		ri[h[r]] = siz, le[siz] = h[r];
	}
}
inline void resume(const int &c) {
	for (int i = dw[c]; i != c; i = dw[i])
		le[ri[i]] = ri[le[i]] = i;	
}
inline void remove(const int &c) {
	for (int i = dw[c]; i != c; i = dw[i])
		le[ri[i]] = le[i], ri[le[i]] = ri[i];
}
inline int Astar() {
	int ret = 0;
	for (int c = ri[0]; c; c = ri[c]) vis[c] = true;
	for (int c = ri[0]; c; c = ri[c])
		if (vis[c]) {
			ret ++;
			vis[c] = false;
			for (int i = dw[c]; i != c; i = dw[i])
				for (int j = ri[i]; j != i; j = ri[j])
					vis[col[j]] = false;
		}
	return ret;
}
bool Dance(int d) {
	if (!ri[0]) return d <= K;
	if (d + Astar() > K) return false;
	int c = ri[0];
	for (int i = ri[0]; i; i = ri[i])
		if (s[i] < s[c]) c = i;
	for (int i = dw[c]; i != c; i = dw[i]) {
		remove(i);
		for (int j = ri[i]; j != i; j = ri[j]) remove(j);
		if (Dance(d + 1)) return true;
		for (int j = le[i]; j != i; j = le[j]) resume(j);
		resume(i);
	}
	return false;
}
struct Point {
	int x, y;
	inline friend double operator * (const Point &r, const Point &s) {
		return sqrt((double)(r.x - s.x) * (r.x - s.x) + (double)(r.y - s.y) * (r.y - s.y));
	}
}a[maxm], b[maxm];
int main() {
	scanf("%d", &T);
	while (T --) {
		scanf("%d%d%d", &N, &M, &K);
		for (int i = 0; i < N; ++ i)
			scanf("%d%d", &a[i].x, &a[i].y);
		for (int i = 0; i < M; ++ i)
			scanf("%d%d", &b[i].x, &b[i].y);
		double lf = 0, rg = 1e8;
		while (rg - lf >= eps) {
			double mid = (lf + rg) / 2;
			init(M, N);
			for (int i = 0; i < M; ++ i)
				for (int j = 0; j < N; ++ j)
					if (b[i] * a[j] < mid - eps)
						link(i + 1, j + 1);
			if(Dance(0)) rg = mid - eps;
			else lf = mid + eps;
		}
		printf("%.6f\n", lf);
	}	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值