hdu 3656 Fire station 重复覆盖 DLX+二分答案 给出一些城市及一些救火站的坐标,要求这些救火站覆盖所有的城市,问从救火站到城市的最长时间至少是多少。

Problem Description
A city's map can be seen as a two dimensional plane. There are N houses in the city and these houses can be seen as N points P 1 …… P N on the two dimensional plane. For simplicity's sake, assume that the time spent from one house number to another is equal to the distance between two points corresponding to the house numbers. The government decides to build M fire stations from N houses. (If a station is build in Pi, We can think the station is next to the house and the time from the station to the house is considered zero.) It is obvious that if some place such as Pi is breaking out of fire, the nearest station will dispatched a fire engine quickly rushed to the rescue scene. The time it takes from this station to the rescue scene is called rescue time. Now you need to consider about a problem that how to choice the positions of the M fire station to minimize the max rescue time of all the houses.
 

Input
The fi rst line of the input contains one integer T, where T is the number of cases. For each case, the fi rst line of each case contains two integers N and M separated by spaces (1 ≤ M ≤N ≤ 50), where N is the number of houses and M is the number of fire stations. Then N lines is following. The ith line contains two integers Xi and Yi (0 ≤ Xi, Yi ≤ 10000), which stands for the coordinate of the ith house.
 

Output
The rescue time which makes the max rescue time is minimum.
 

Sample Input
  
  
2 4 2 1 1 1 2 2 3 2 4 4 1 1 1 1 2 2 3 2 4
 

Sample Output
  
  
1.000000 2.236068


//


#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define eps 1e-8
#define N 85
#define V 36000
int n,m,K;//n行 m列 K限制条件,最多用K行(仅限于此题)
int L[V],R[V];
int D[V],U[V];
int C[V];
int S[N],H[N];
int ak,size;//ak 最少多少行可以覆盖所有列(可重复)
double dis(double x1,double y1,double x2,double y2)
{
    return sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}
void Link(int r,int c)
{
    S[c]++;C[size]=c;
    U[size]=U[c];D[U[c]]=size;
    D[size]=c;U[c]=size;
    if(H[r]==-1) H[r]=L[size]=R[size]=size;
    else
    {
        L[size]=L[H[r]];R[L[H[r]]]=size;
        R[size]=H[r];L[H[r]]=size;
    }
    size++;
}
void remove(int c)
{
    int i;
    for(i=D[c];i!=c;i=D[i])
        L[R[i]]=L[i],R[L[i]]=R[i];
}
void resume(int c)
{
    int i;
    for(i=U[c];i!=c;i=U[i])
        L[R[i]]=R[L[i]]=i;
}
int h()
{
    int i,j,k,count=0;
    bool visit[N];
    memset(visit,0,sizeof(visit));
    for(i=R[0];i;i=R[i])
    {
        if(visit[i]) continue;
        count++;
        visit[i]=1;
        for(j=D[i];j!=i;j=D[j])
        {
            for(k=R[j];k!=j;k=R[k])
                visit[C[k]]=1;
        }
    }
    return count;
}
void Dance(int k)
{
    int i,j,c,Min,ans;
    ans=h();
    if(k+ans>K) return ;//仅限于此题
    if(k+ans>=ak) return;
    if(!R[0])
    {
        if(k<ak) ak=k;
        return;
    }
    for(Min=N,i=R[0];i;i=R[i])
        if(S[i]<Min) Min=S[i],c=i;
    for(i=D[c];i!=c;i=D[i])
    {
        remove(i);
        for(j=R[i];j!=i;j=R[j])
            remove(j);
        Dance(k+1);
        for(j=L[i];j!=i;j=L[j])
            resume(j);
        resume(i);
    }
    return;
}
double d[66][66];
double dl[66*66];
int main()
{
    int i,j,ncase;
    double x[N],y[N];
    int left,right,mid;
    double ans;
    scanf("%d",&ncase);
    while(ncase--)
    {
        int tn;
        //村庄当成列tn,救火站当成行tn  最多用K个救火站
        scanf("%d%d",&tn,&K);
        n=m=tn;
        for(i=1;i<=m;i++)
            scanf("%lf%lf",&x[i],&y[i]);
        int num = 0;
for (i = 1; i <= n; ++i) {
for (j = i; j <= m; ++j) {
d[i][j] = d[j][i] = dl[num++] = dis(x[i],y[i],x[j], y[j]);
}
}
//二分答案这里要注意:如果直接left=0,right=(1<<30)直接二分 会超时
//这里要把所有距离先排序 二分枚举这些距离  AC
sort(dl, dl + num);
num = unique(dl, dl + num) - dl;
        left=0;
        right= num-1;
        ans=dl[right];
        while(left<right)
        {
            //DLX
            for(i=0;i<=m;i++)
            {
                S[i]=0;
                U[i]=D[i]=i;
                L[i+1]=i;R[i]=i+1;
            }R[m]=0;
            memset(H,-1,sizeof(H));
            size=m+1;
            mid=(left+right)/2;
            for(i=1;i<=n;i++)
            {
                for(j=1;j<=m;j++)
                    if(dl[mid]>=d[i][j])  Link(i,j);
            }
            ak=N;
            Dance(0);
            //......
            if(ak<=K) {ans=min(ans,dl[mid]);right=mid;}
            else left=mid+1;
        }
        printf("%.6lf\n",ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值