HDU3656-Fire station

19 篇文章 0 订阅

Fire station

                                                                  Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
                                                                                            Total Submission(s): 1733    Accepted Submission(s): 585


Problem Description
A city's map can be seen as a two dimensional plane. There are N houses in the city and these houses can be seen as N points P 1 …… P N on the two dimensional plane. For simplicity's sake, assume that the time spent from one house number to another is equal to the distance between two points corresponding to the house numbers. The government decides to build M fire stations from N houses. (If a station is build in Pi, We can think the station is next to the house and the time from the station to the house is considered zero.) It is obvious that if some place such as Pi is breaking out of fire, the nearest station will dispatched a fire engine quickly rushed to the rescue scene. The time it takes from this station to the rescue scene is called rescue time. Now you need to consider about a problem that how to choice the positions of the M fire station to minimize the max rescue time of all the houses.
 

Input
The fi rst line of the input contains one integer T, where T is the number of cases. For each case, the fi rst line of each case contains two integers N and M separated by spaces (1 ≤ M ≤N ≤ 50), where N is the number of houses and M is the number of fire stations. Then N lines is following. The ith line contains two integers Xi and Yi (0 ≤ Xi, Yi ≤ 10000), which stands for the coordinate of the ith house.
 

Output
The rescue time which makes the max rescue time is minimum.
 

Sample Input
  
  
2 4 2 1 1 1 2 2 3 2 4 4 1 1 1 1 2 2 3 2 4
 

Sample Output
  
  
1.000000 2.236068
 

Author
wwr & fengzlzl
 

Source
 


题意:有n个城市在二维平面上,要选一些城市建消防站,不超过m个,消防站要把所有城市覆盖住,那么覆盖半径最小为多少。如果某个城市有消防站,那么这个城市和消防站的距离为0

解题思路:二分+舞蹈链,每次二分到一个距离,判断每个城市建消防站可以覆盖哪些城市,然后用舞蹈链的重复覆盖判断出最少需要几个城市


#include <iostream>    
#include <cstdio>    
#include <cstring>    
#include <string>    
#include <algorithm>    
#include <cctype>    
#include <map>    
#include <cmath>    
#include <set>    
#include <stack>    
#include <queue>    
#include <vector>    
#include <bitset>    
#include <functional>    

using namespace std;

#define LL long long    
const int INF = 0x3f3f3f3f;
const int maxn = 500005;

int n, m, x[55], y[55], tot;
double dis[3000];

struct DLX
{
	int L[maxn], R[maxn], U[maxn], D[maxn];
	int row[maxn], col[maxn], sum[maxn], ans[maxn];
	int n, m, cnt, num;
	int vis[maxn];
	void add(int k, int l, int r, int u, int d, int x, int y)
	{
		L[k] = l;   R[k] = r;   U[k] = u;
		D[k] = d;   row[k] = x;  col[k] = y;
	}
	void reset(int n, int m)
	{
		num = 0x7FFFFFFF;
		this->n = n;   this->m = m;
		for (int i = 0; i <= m; i++)
		{
			add(i, i - 1, i + 1, i, i, 0, i);
			sum[i] = 0;
		}
		L[0] = m, R[m] = 0, cnt = m + 1;
	}
	void insert(int x, int y)
	{
		int temp = cnt - 1;
		if (row[temp] != x)
		{
			add(cnt, cnt, cnt, U[y], y, x, y);
			U[D[cnt]] = cnt; D[U[cnt]] = cnt;
		}
		else
		{
			add(cnt, temp, R[temp], U[y], y, x, y);
			R[L[cnt]] = cnt; L[R[cnt]] = cnt;
			U[D[cnt]] = cnt; D[U[cnt]] = cnt;
		}
		sum[y]++, cnt++;
	}
	void Remove(int k)
	{
		for (int i = D[k]; i != k; i = D[i])
		{
			L[R[i]] = L[i];
			R[L[i]] = R[i];
		}
	}
	void Resume(int k)
	{
		for (int i = U[k]; i != k; i = U[i]) L[R[i]] = R[L[i]] = i;
	}
	int A()
	{
		int dis = 0;
		for (int i = R[0]; i != 0; i = R[i]) vis[i] = 0;
		for (int i = R[0]; i != 0; i = R[i])
			if (!vis[i])
			{
				dis++, vis[i] = 1;
				for (int j = D[i]; j != i; j = D[j])
					for (int k = R[j]; k != j; k = R[k])
						vis[col[k]] = 1;
			}
		return dis;
	}
	void Dfs(int k)
	{
		if (!R[0]) { num = min(num, k); return ; }
		else if (k + A() < num)
		{
			int now = R[0];
			for (int i = R[0]; i != 0; i = R[i])
				if (sum[now] > sum[i]) now = i;
			for (int i = D[now]; i != now; i = D[i])
			{
				Remove(i);
				for (int j = R[i]; j != i; j = R[j]) Remove(j);
				Dfs(k + 1);
				for (int j = L[i]; j != i; j = L[j]) Resume(j);
				Resume(i);
			}
		}
	}
}dlx;

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &n, &m);
		tot = 0;
		dis[tot++] = 0;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d%d", &x[i], &y[i]);
			for (int j = 1; j < i; j++)
				dis[tot++] = sqrt((x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]));
		}
		sort(dis, dis + tot);
		tot = unique(dis, dis + tot) - dis;
		int l = 0, r = tot - 1, ans;
		while (l <= r)
		{
			int mid = (l + r) >> 1;
			dlx.reset(n, n);
			for (int i = 1; i <= n; i++)
				for (int j = 1; j <= n; j++)
				{
					double temp = sqrt((x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]));
					if (dis[mid] >= temp) dlx.insert(i, j);
				}
			dlx.Dfs(0);
			if (dlx.num <= m) r = mid - 1, ans = mid;
			else l = mid + 1;
		}
		printf("%.6lf\n", dis[ans]);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值