HDU-5046 Airport(二分+DLX重复覆盖)

题意:

在N个城市选出K个城市,建飞机场(1 ≤ N ≤ 60,1 ≤ K ≤ N),N个城市给出坐标,选择这K个机场,使得从城市到距离自己最近的机场的 最大的距离 最小。输出这个最小值。

思路:

将所有的二维坐标抽象成一个无向完全图,然后我们可以去二分这个答案最大距离,每次将大于当前二分值的边从图中去掉。注意这题的两点距离是曼哈顿距离,代表着任意两点之间的距离必定大于等于它们之间再经过一个点的距离。我们把n个点看成n*n的矩阵,所以在新图中,如果一个点u能够到达点v,说明矩阵第u行能覆盖第v列,两两之间就构成了矩阵的一个1,从而问题就转化为了寻找最少的行覆盖所有的列,问题就成为了跳舞链去求重复覆盖。

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxnode = 5010;
const int maxn = 70;
const int maxm = 70;
const ll up = 4e9;
pair<ll, ll> pr[70];
vector<int> G[70];
int t, n, k;
struct DLX {
	int n, m, size;
	int row[maxnode], col[maxnode];
	int U[maxnode], D[maxnode], L[maxnode], R[maxnode];
	int H[maxn], S[maxm];
	int ansd;
	bool vis[maxm];
	void init(int _n, int _m)
	{
		n = _n, m = _m, size = _m, ansd = inf;
		for(int i = 0; i <= m; ++i)
		{
			S[i] = 0;
			U[i] = D[i] = i;
			L[i] = i-1, R[i] = i+1; 
		}
		L[0] = m, R[m] = 0;
		for(int i = 1; i <= n; ++i) H[i] = -1;
	} 
	void Link(int r, int c)
	{
		++S[col[++size] = c];
		row[size] = r;
		D[size] = D[c];
		U[D[c]] = size;
		U[size] = c;
		D[c] = size;
		if(H[r] < 0) H[r] = L[size] = R[size] = size;
		else
		{
			R[size] = R[H[r]];
			L[R[H[r]]] = size;
			L[size] = H[r];
			R[H[r]] = size;
		}
	}
	void remove(int c)
	{
		for(int i = D[c]; i != c; i = D[i])
		L[R[i]] = L[i], R[L[i]] = R[i];
	}
	void resume(int c)
	{
		for(int i = U[c]; i != c; i = U[i])
		L[R[i]] = R[L[i]] = i;
	}
	int h()
	{
		int ret = 0;
		memset(vis, 0, sizeof vis);
		for(int i = R[0]; i; i = R[i])
		{
			if(vis[i]) continue;
			++ret; vis[i] = true;
			for(int j = D[i]; j != i; j = D[j])
			for(int k = R[j]; k != j; k = R[k])
			vis[col[k]] = true;
		}
		return ret;
	}
	void dance(int d)
	{
		if(d+h() >= ansd) return;
		if(R[0] == 0)
		{
			if(d < ansd) ansd = d;
			return;
		} 
		int c = R[0];
		for(int i = R[0]; i; i = R[i])
		if(S[i] < S[c]) c = i;
		for(int i = D[c]; i != c; i = D[i])
		{
			remove(i);
			for(int j = R[i]; j != i; j = R[j])
				remove(j);
			dance(d+1);
			for(int j = L[i]; j != i; j = L[j])
				resume(j);
			resume(i); 	
		} 
		return;
	}
} dlx;

bool jg(ll x)
{
	for(int i = 1; i <= n; ++i)
	G[i].clear();
	for(int i = 1; i <= n; ++i)
	for(int j = i+1; j <= n; ++j)
	{
		ll tmp = abs(pr[i].first-pr[j].first)+abs(pr[i].second-pr[j].second);
		if(tmp <= x)
		G[i].push_back(j), G[j].push_back(i);
	}
	dlx.init(n, n);
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 0; j < G[i].size(); ++j)
		dlx.Link(i, G[i][j]);
		dlx.Link(i, i);
	}
	dlx.dance(0);
	if(dlx.ansd <= k) return true;
	return false;
}
void work()
{
	ll mid, l = 0, r = up;
	while(l <= r)
	{
		mid = (l+r)/2;
		if(jg(mid)) r = mid-1;
		else l = mid+1;
	}
	printf("%lld\n", l);
}
int main()
{
	//freopen("in.txt", "r", stdin);
	scanf("%d", &t);
	for(int _ = 1; _ <= t; ++_)
	{
		scanf("%d %d", &n, &k);
		for(int i = 1; i <= n; ++i)
		scanf("%lld %lld", &pr[i].first, &pr[i].second);
		printf("Case #%d: ", _);
		work();
	}
	return 0;
}


继续加油~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值