hdu 2295 Radar(重复覆盖,二分+DLX)

题意:有n个城市,m个工作站,工作站的覆盖区域呈圆状,且有相同的半径,最多只能使用m个工作站,问可以覆盖掉所有的城市的工作站半径最小为多少。

思路:二分加DLX。采用二分半径的方法,对于每一个半径,构造一个十字链,列为n个城市,行为m个工作站,对于每一行来讲为当前半径下,工作站可以覆盖掉的城市。然后进行A*即可。

tirick:1)一个是对于选取最小行时,设置的最大值比较小。WA。

2)Astar时从下往上迭代TLE,从上往下迭代AC。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define inf 0x3f3f3f
#define eps 1e-7//精度控制范围
using namespace std;
const int maxn=64*64+64;
struct nn
{
    int x;
    int y;
};
nn p[100];
nn p2[100];
struct node2
{
    int l,r;
    int u,d;
    int s,c;
};
node2 dlx[maxn];
int h[64];
int head;
int size;
bool hash[64];
int limit;
int n,m;
double dis(nn a,nn b)
{
    return ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool check(nn a,nn b,double r)//判断在不在圆内
{
    double tmp;
    tmp=dis(a,b);
    if(tmp<=r)
    {
        return true;
    }
    return false;
}
void init(int r,int c)//  初始化
{
    head=0;
    for(int i=0; i<=c; i++)
    {
        dlx[i].s=0;
        dlx[i].u=dlx[i].d=i;
        dlx[i].r=i+1;
        dlx[i+1].l=i;
    }
    size=c;
    dlx[c].r=0;
    while(r)
    {
        h[r--]=-1;
    }
}
void link(int r,int c)
{
    int tmp=++size;
    dlx[c].s++;
    dlx[tmp].c=c;
    dlx[tmp].d=dlx[c].d;
    dlx[tmp].u=c;
    dlx[dlx[c].d].u=tmp;
    dlx[c].d=tmp;
    if(h[r]<0)
    {
        h[r]=dlx[tmp].l=dlx[tmp].r=tmp;
    }
    else
    {
        dlx[tmp].r=dlx[h[r]].r;
        dlx[tmp].l=h[r];
        dlx[dlx[h[r]].r].l=tmp;
        dlx[h[r]].r=tmp;
    }
}
void remove(int x)
{
    for(int i=dlx[x].d; i!=x; i=dlx[i].d)
    {
        dlx[dlx[i].r].l=dlx[i].l;
        dlx[dlx[i].l].r=dlx[i].r;
        dlx[dlx[i].c].s--;
    }
}
void resume(int x)
{
    for(int i=dlx[x].u; i!=x; i=dlx[i].u)
    {
        dlx[dlx[i].r].l=i;
        dlx[dlx[i].l].r=i;
        dlx[dlx[i].c].s++;
    }
}
int hh()
{
    int ret=0;
    memset(hash,0,sizeof(hash));
    for(int i=dlx[head].r; i!=head; i=dlx[i].r)
    {
        if(hash[i]==false)
        {
            hash[i]=true;
            ret++;
        for(int j=dlx[i].d; j!=i; j=dlx[j].d)
            for(int k=dlx[j].r; k!=j; k=dlx[k].r)
            {
                hash[dlx[k].c]=true;
            }
        }
    }
    //cout<<"er"<<endl;
    return ret;
}
bool dlx_dfs(int k)//舞蹈链
{
    if(k+hh()>limit)
    {
        return false;
    }
    if(dlx[head].r==head)
    {
        if(k<limit) limit=k;
        return true;
    }
    int minn=m+1;
    int tt;
    for(int i=dlx[head].r; i!=head; i=dlx[i].r)
    {
        if(minn>dlx[i].s)
        {
            minn=dlx[i].s;
            tt=i;
        }
    }
    for(int i=dlx[tt].d; i!=tt; i=dlx[i].d)
    {
        remove(i);
        for(int j=dlx[i].r; j!=i; j=dlx[j].r)
        {
            remove(j);
        }
        if(dlx_dfs(k+1))
        {
            return true;
        }
        for(int j=dlx[i].l; j!=i; j=dlx[j].l)
        {
            resume(j);
        }
        resume(i);
    }
    return false;
}
int astar(int x)//astar迭代
{
    limit=x;//限制为最大的K。
    while(dlx_dfs(0)==true)
    {
        limit--;
        if(limit==0)
        {
            break;
        }
    }
    if(limit==x) return x+1;
    else return limit+1;
}
void pre(double r)
{
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            if(check(p[j],p2[i],r))
            {
                link(i,j);
            }
        }
}
int main()
{
   // freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    int k;
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&p[i].x,&p[i].y);
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&p2[i].x,&p2[i].y);
        }
        double l,r;
        l=0.000001;
        r=1416.0;
        int tt;
        while(l+eps<=r)//二分答案
        {
            double mid=(l+r)*0.5;
            init(m,n);
            pre(mid*mid);
            tt=astar(k);
            if(tt<=k)
            {
                r=mid;
            }
            else
            {
                l=mid;
            }
        }
        printf("%.6f\n",r);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值