hdu2295(DLX重复覆盖)

本文介绍了一种使用二分法搜索最优解,并通过 Dancing Links (DLX) 算法解决可重复集合覆盖问题的方法。具体应用在给定半径下寻找能够覆盖所有点的最少圆的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很容易想到,二分答案。

每次根据半径建好0,1矩阵。

然后就是DLX可重复覆盖的模板

#include<cstdio>
#include<cstring>
#include<cmath>
double x[55],y[55],X[55],Y[55];
const int MN=1005;
const int MM=1005;
const int MNN=1e5+5+MM; //最大点数
const double eps = 1e-9;
int n,m,k;
struct DLX
{
    int n,m,size;
    int U[MM],D[MM],R[MM],L[MM],Row[MM],Col[MM];
    int H[MM],S[MM];
    int ands,ans[MM];
    void init(int _n,int _m)
    {
        n = _n;
        m = _m;
        for(int i = 0;i <= m;i++)
        {
            S[i] = 0;
            U[i] = D[i] = i;
            L[i] = i-1;
            R[i] = i+1;
        }
        R[m] = 0; L[0] = m;
        size = m;
        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;
    }
    bool v[MM];
    int f()
    {
        int ret = 0;
        for(int c = R[0];c != 0;c = R[c])v[c] = true;
        for(int c = R[0];c != 0;c = R[c])
            if(v[c])
            {
                ret++;
                v[c] = false;
                for(int i = D[c];i != c;i = D[i])
                    for(int j = R[i];j != i;j = R[j])
                        v[Col[j]] = false;
            }
        return ret;

    }
    bool Dance(int d)
    {
        if(d + f() > k)return false;
        if(R[0] == 0)return d <= k;
        int c = R[0];
        for(int i = R[0];i != 0;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(d+1))return true;
            for(int j = L[i];j != i;j = L[j])resume(j);
            resume(i);
        }
        return false;
    }
}dlx;
double cla(int a,int b)
{
    return sqrt((X[a]-x[b])*(X[a]-x[b])+(Y[a]-y[b])*(Y[a]-y[b]));
}
bool check(double mid)
{
    dlx.init(m,n);
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(cla(i,j)-mid<=eps)
                dlx.Link(i,j);
        }
    }
    dlx.ands = -1;
    if(dlx.Dance(0)) return true;
    return false;
}
int main()
{
    int cases;
    scanf("%d",&cases);
    while(cases--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
            scanf("%lf%lf",&x[i],&y[i]);
        for(int i=1;i<=m;i++)
            scanf("%lf%lf",&X[i],&Y[i]);
        double l = 0,r = 10000000,mid;
        for(int i=0;i<100;i++)
        {
            mid = (l+r)/2;
            bool flag = check(mid);
            //printf("%f %d\n",mid,flag);
            if(flag) r = mid-eps;
            else l = mid+eps;
        }
        printf("%.6lf\n",r);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值