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

36 篇文章 4 订阅



题意:

给定 n 个城市的坐标,要在城市中建 k 个飞机场,使城市距离最近的飞机场的最长距离最小,求这个最小距离。

分析:

最小化最大值,显然二分最大距离。然后我们将距离在范围内的两个城市建边,建一个n*n的矩阵,看能否选出 k 个城市,使得覆盖了所有城市。 将点之间的关系转化成01矩阵的覆盖问题,每一行代表一个城市可以覆盖到的城市,重复覆盖,建好边套个 DLX 即可。 
看了鸟神博客,这里可以直接将所有距离保存在一个数组中,排序并去重,二分下标即可。这样快了很多很多! 

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 3600 + 5, maxc = 60 + 5, maxr = 60 + 5, inf = 0x3f3f3f3f;
struct Node{ll x; ll y;}city[maxn];
int L[maxn], R[maxn], D[maxn], U[maxn], C[maxn];
 // 记录某个标号的节点的上下左右节点的编号
int S[maxc], H[maxr], sz;
  // H记录每行节点的信息(无行头),S保存某一列1的数量
int n, m, k;
ll d[maxr][maxc], t[maxn];
///不需要S域
void init(int x)///col is 1~x,row start from 1
{
    for (int i = 0; i <= x; ++i)
    {
        S[i] = 0;
        D[i] = U[i] = i;
        L[i+1] = i; R[i] = i+1;
    }///对列表头初始化
    R[x] = 0;
    sz = x + 1;///真正的元素从m+1开始
    memset (H, -1, sizeof(H));
    ///mark每个位置的名字
}
void Link(int r, int c)
{
    S[c]++; C[sz] = c;
    U[sz] = U[c]; D[U[c]] = sz;
    D[sz] = c; U[c] = sz;
    U[c] = sz;
    if(H[r] == -1) H[r] = L[sz] = R[sz] = sz;
    else
    {
        L[sz] = L[H[r]]; R[L[H[r]]] = sz;
        R[sz] = H[r]; L[H[r]] = sz;
    }
    sz++;
}
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;
    bool vis[maxc];
    memset (vis, false, 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[C[k]] = true;
    }
    return ret;
}
int ans;
bool Dance(int a) //具体问题具体分析, a是递归层数
{
    if(a + h() > k) return 0;  //如果 当前层数+即将要加的层数 > 说明肯定不可以
    if(!R[0]) return a <= k;  //说明出答案了
    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);
        if(Dance(a + 1)) return 1;
        // 这里必须反着恢复
        for (int j = L[i]; j != i; j = L[j])
            resume(j);
        resume(i);
    }
    return 0;
}
ll dist(int i, int j)
{
    ll d = abs(city[i].x - city[j].x) +  abs(city[i].y - city[j].y);
    return d;
}
bool judge(ll mid)
{
    init(n);
    ans = 0x3f3f3f3f;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
            if(d[i][j] <= mid)
                Link(i, j);
    }
    return Dance(0);  //从0层开始
}
int main (void)
{
    int T, ca = 1;
    cin >> T;
    while(T--)
    {
        scanf("%d%d", &n, &k);
        ll maxd = 0;
        int cnt = 0;
        for(int i = 1; i <= n; i++)
            scanf("%I64d%I64d", &city[i].x, &city[i].y);
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
            {
                d[i][j] = dist(i, j);
                t[cnt++] = d[i][j];
            }
        sort(t, t + cnt);
        int ncnt = unique(t, t + cnt) - t;  //去重, 二分所有可能的距离更好
        ll l = -1, r = ncnt;
        while(r - l > 1)
        {
            ll mid = (l + r) / 2;
            if(judge(t[mid])) r = mid;
            else l = mid;
        }
        printf("Case #%d: %I64d\n", ca++, t[r]);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值