hdu3656Fire station(DLX重复覆盖 + 二分)

来源:互联网 发布:720软件百科 编辑:程序博客网 时间:2024/06/07 02:15

题目请戳这里

题目大意:一个城市n个点,现在要建m个消防站,消防站建在给定的n个点中。求建m个消防站后,m个消防站要覆盖所有的n个点的覆盖半径最小。

题目分析:重复覆盖问题,DLX解决。不过要求覆盖半径最小,需要二分。虽然给的范围并不大,DLX毕竟还是暴力搜索,而且精度有6位小数,因此直接二分距离的话会TLE!解决方案是将图中任意2点的距离记录下来,去重后二分已知的距离。因为消防站建在给定的n个点中,那么最小覆盖半径一定在任意2点距离中产生。

DLX搜索的时候,一般习惯删除的时候从左往右,不过效率却不一定高。比如这题,从左向右删除和从右向左删除,跑的时间至少差了1s以上!可见用dancing links搜索的时候姿势还是很重要的。有时候换个姿势也许效率更高~

详情请见代码:

#include <iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;const int N = 51;const int M = 30001;const double eps = 1e-7;double dis[N][N];double tle[M];int s[M],h[M],u[M],d[M],l[M],r[M],col[M],row[M];int point[N][2];int m,n,num,len;int fs;double getdis(int i,int j){    return sqrt((double)(point[i][0] - point[j][0])*(point[i][0] - point[j][0])               +(double)(point[i][1] - point[j][1])*(point[i][1] - point[j][1]));}void read(){    scanf("%d%d",&n,&m);    int i,j;    len = 1;    tle[len ++] = 0;    for(i = 1;i <= n;i ++)    {        dis[i][i] = 0.0;        scanf("%d%d",&point[i][0],&point[i][1]);        for(j = 1;j < i;j ++)            dis[i][j] = dis[j][i] = getdis(i,j),tle[len ++] = dis[i][j];    }}void init(){    memset(h,0,sizeof(h));    memset(s,0,sizeof(s));    for(int i = 0;i <= n;i ++)    {        u[i] = d[i] = i;        l[i] = (i + n) % (n + 1);        r[i] = (i + 1) % (n + 1);    }    num = n + 1;}void add(int i,int j){    if(h[i])    {        r[num] = h[i];        l[num] = l[h[i]];        r[l[num]] = l[r[num]] = num;    }    else        h[i] = l[num] = r[num] = num;    s[j] ++;    u[num] = u[j];    d[num] = j;    d[u[num]] = num;    u[j] = num;    col[num] = j;    row[num] = i;    num ++;}void build(double md){    int i,j;    init();    for(i = 1;i <= n;i ++)        for(j = 1;j <= n;j ++)            if(md - dis[i][j] > -eps)                add(i,j);}void remove(int c){    for(int i = d[c];i != c;i = d[i])        l[r[i]] = l[i],r[l[i]] = r[i],s[col[i]] --;}void resume(int c){    for(int i = u[c];i != c;i = u[i])        l[r[i]] = r[l[i]] = i,s[col[i]] ++;}int A(){    int i,j,k,ret = 0;    bool vis[N];    memset(vis,false,sizeof(vis));    for(i = l[0];i;i = l[i])    {        if(vis[i] == false)        {            vis[i] = true;            ret ++;            for(j = d[i];j != i;j = d[j])                for(k = r[j];k != j;k = r[k])                    vis[col[k]] = true;        }    }    return ret;}void dfs(int k){    if(k + A() >= fs)        return;    int i,j;    if(!r[0])    {        fs = min(fs,k);        return;    }    int mn = 1000000;    int c;    for(i = l[0];i;i = l[i])    {        if(mn > s[i])        {            mn = s[i];            c = i;        }    }    for(i = d[c];i != c;i = d[i])    {        remove(i);        for(j = l[i];j != i;j = l[j])        {            remove(j);        }        dfs(k + 1);        for(j = r[i];j != i;j = r[j])        {            resume(j);        }        resume(i);    }}void solve(){    int la,ra,mid,ans;    sort(tle + 1,tle + len);    int i = len;    int j;    len = 2;    for(j = 2;j < i;j ++)        if(fabs(tle[j] - tle[j - 1]) > eps)            tle[len ++] = tle[j];    len --;    la = 1;ra = len;    while(la <= ra)    {        mid = (la + ra)>>1;        build(tle[mid]);        fs = M;        dfs(0);        if(fs <= m)        {            ans = mid;            ra = mid - 1;        }        else            la = mid + 1;    }    printf("%f\n",tle[ans]);}int main(){    int _;    scanf("%d",&_);    while(_ --)    {        read();        solve();    }    return 0;}//1953MS  636K


原创粉丝点击