题意:
在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;
}
继续加油~