HDU-5046___Airport —— 二分 + DLX +剪枝

题目链接:点我啊╭(╯^╰)╮

题目大意:

     给定 n n n 个城市的坐标,要在这 n n n 个城市里建 k k k 个飞机场,要使任意城市离最近的飞机场距离的最大值最小

解题思路:

    最大值最小化,很典型的二分,即二分城市与飞机场的距离,若满足 k k k 个以内的飞机场在这个距离内能覆盖所有城市,则减小这个二分值
    那么我们就解决了怎么得到这个最小值,但问题的关键是怎么去判断k个飞机场能否覆盖所有城市,这里的思路几乎与HDU 2295是同一个套路
    我们用二分的距离去判断可行性,若在两座城市的距离在这个距离内,那么就在DLX模型中建边,则转化为了可重复覆盖的问题

代码思路:

    如果直接套模板的话不知道能不能过,在本题内有两个除模板以外的两个优化:
①:因为我们要判断的是能否在 k k k 行内覆盖完,所以在 dfs 的过程中对深度与k进行比较,加速返回
②:在本题中直接对距离的最大值进行二分是不足够好的,看了别人的题解之后发现可以对所有的距离值进行二分,思路是:先把所有城市的距离用数组存下来,排序+去重,然后直接二分数组下标即可,极大的优化了时间复杂度

核心:二分 + DLX 的好题,对优化要做到位

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k,cas,t=1;
pair <ll,ll> city[65];
ll dis[65][65], val[3650];

struct DancingLink{
	const static int N = 500010;
	const static int M = 1010;
    int n, s, ansd;	// 列数 节点总数 
    int S[M], A[M], H[M];	// S[]该列节点总数  A[]答案  H[]行首指针 
    int L[N], R[N], U[N], D[N];	// L[],R[],U[],D[] 上下左右 
    int X[N], C[N], vis[M];	// X[] C[] 行列编号 
    void init(int n){	// 初始化 
        this->n = n;
        for(int i=0; i<=n; i++)
            U[i]=i, D[i]=i, L[i]=i-1, R[i]=i+1;
        R[n]=0, L[0]=n; s=n+1;
        memset(S, 0, sizeof(S));
        memset(H, -1, sizeof(H));
    }
    void DelCol(int c){	// 删除列 
        for(int i=D[c]; i!=c; i=D[i])
            L[R[i]]=L[i], R[L[i]]=R[i];
    }
    void ResCol(int c){	// 恢复列 
        for(int i=U[c]; i!=c; i=U[i])
            L[R[i]]=R[L[i]]=i;
    }
    void AddNode(int r,int c){	// 添加节点 
        ++S[c], C[++s]=c, X[s]=r;
        D[s]=D[c], U[D[c]]=s, U[s]=c, D[c]=s;
        if(H[r]<0) H[r]=L[s]=R[s]=s;	// 行首节点
        else R[s]=R[H[r]], L[R[H[r]]]=s, L[s]=H[r], R[H[r]]=s;
    }
    int f(){
        int ret=0;
        memset(vis, 0, sizeof(vis));
        for(int i=R[0]; i; i=R[i])
            if(!vis[i]){
                ret++; vis[i]=1;
                for(int j=D[i]; j!=i; j=D[j])
                    for(int k=R[j]; k!=j; k=R[k])
                        vis[C[k]]=1;    
            }
        return ret;
    }
    bool dfs(int d){	// 深度,深搜遍历 
        if(d+f() > k) return 0;
        if(!R[0]) return d <= 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]){
            DelCol(i); A[d]=X[i];
            for(int j=R[i]; j!=i; j=R[j]) DelCol(j);
            if(dfs(d+1)) return 1;
            for(int j=L[i]; j!=i; j=L[j]) ResCol(j);
            ResCol(i);
        }
        return 0;
    }
} dlx; 

bool check(ll w) {
	dlx.init(n);
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			if(dis[i][j]<=w)
				dlx.AddNode(i,j);
	return dlx.dfs(0);
}

int main() {
	scanf("%d", &cas);
	while(cas--) {
		scanf("%d%d", &n, &k);
		int cnt = 0;
		for(int i=1; i<=n; i++) {
			scanf("%lld%lld", &city[i].first, &city[i].second);
			for(int j=1; j<=i; j++) {
				dis[i][j]=dis[j][i]=val[cnt++]=\
				fabs(city[i].first-city[j].first)+\
				fabs(city[i].second-city[j].second);
			}
		}
		sort(val, val+cnt);
		cnt = unique(val, val + cnt) - val;
		int l = 0, r = cnt-1, mid;
		while(l<=r) {
			mid = (l+r)/2;
			if(check(val[mid])) r = mid - 1;
			else l = mid + 1;
		}
		printf("Case #%d: %lld\n", t++, val[r+1]);
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值