Dancing Links--- B - Radar

N cities of the Java Kingdom need to be covered by radars for being in a state of war. Since the kingdom has M radar stations but only K operators, we can at most operate K radars. All radars have the same circular coverage with a radius of R. Our goal is to minimize R while covering the entire city with no more than K radars.
Input
The input consists of several test cases. The first line of the input consists of an integer T, indicating the number of test cases. The first line of each test case consists of 3 integers: N, M, K, representing the number of cities, the number of radar stations and the number of operators. Each of the following N lines consists of the coordinate of a city.
Each of the last M lines consists of the coordinate of a radar station.

All coordinates are separated by one space.
Technical Specification

  1. 1 ≤ T ≤ 20
  2. 1 ≤ N, M ≤ 50
  3. 1 ≤ K ≤ M
  4. 0 ≤ X, Y ≤ 1000
    Output
    For each test case, output the radius on a single line, rounded to six fractional digits.
    Sample Input
    1
    3 3 2
    3 4
    3 1
    5 4
    1 1
    2 2
    3 3
    Sample Output
    2.236068

二分+重复覆盖
很明显是01矩阵的行是雷达站,列是城市。。。
但是每一个R都得构造一个矩阵,而且R还是实数。。。
所以用二分R知道找到一个确定最小的R,对每一个mid,如果R = mid时存在一个重复覆盖,并且不会超过k个雷达站,R向下取,否则向上取。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const double eps = 1e-8;

//最大行数
const int MN = 1005;
//最大列数
const int MM = 1005;
//最大点数
const int MNN = 1e5 + 5 + MM;

struct DLX
{
    //一共n行m列,s个节点
    int n,m,s;
    //交叉十字链表组成部分
    //第i个节点的上U下D左L右R,所在位置row行col列
    int U[MNN],D[MNN],L[MNN],R[MNN],row[MNN],col[MNN];
    //H数组记录行选择指针,S数组记录覆盖个数
    int H[MN],S[MM];
    //res记录行个数,ans数组记录可行解
    int res,ans[MN];
    //初始化空表
    void init(int x,int y)
    {
        n = x,m = y;
        //其中0节点作为head节点,其他作为列首节点
        for(int i = 0;i <= m;++i){
            U[i] = D[i] = i;
            L[i] = i - 1;
            R[i] = i + 1;
        }
        R[m] = 0;L[0] = m;
        s = m;
        memset(S,0,sizeof(S));
        memset(H,-1,sizeof(H));
    }
    void Insert(int r,int c)
    {
        //节点数加一,设置s节点所处位置,以及S列覆盖个数加一
        s++;row[s] = r;col[s] = c;S[c]++;
        //将s节点插入对应列中
        D[s] = D[c];U[D[c]] = s;
        U[s] = c;D[c] = s;
        if(H[r] < 0){//如果该行没有元素,H[r]标记该行起始节点
            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;
        }
    }
    //精确覆盖
    void Remove(int c)
    {
        //删除c列
        L[R[c]] = L[c];R[L[c]] = R[c];
        //删除该列上的元素对应的行
        for(int i = D[c];i != c;i = D[i]){//枚举该列元素
            for(int j = R[i];j != i;j = R[j]){//枚举列的某个元素所在行遍历
                U[D[j]] = U[j];
                D[U[j]] = D[j];
                //将该列上的S数组减一
                --S[col[j]];
            }
        }
    }
    void resume(int c)
    {
        //恢复c列
        for(int i = U[c];i != c;i = U[i]){//枚举该列元素
            for(int j = L[i];j != i;j = L[j]){
                U[D[j]] = j;D[U[j]] = j;
                ++S[col[j]];
            }
        }
        L[R[c]] = c;R[L[c]] = c;
    }
    bool dance(int deep)
    {
        if(res < deep) return false;
        //当矩阵为空时,说明找到一个可行解,算法终止
        if(R[0] == 0){
            res = min(res,deep);
            return true;
        }
        //找到节点数最少的列,枚举这列上的所有行
        int c = R[0];
        for(int i = R[0];i != 0;i = R[i]){
            if(S[i] < S[c]){
                c = i;
            }
        }
        //删除节点数最少的列
        Remove(c);
        for(int i = D[c];i != c;i = D[i]){
            //将行r放入当前解
            ans[deep] = row[i];
            //行上节点对应的列上进行删除
            for(int j = R[i];j != i;j = R[j])
                Remove(col[j]);
            //进入下一层
            dance(deep + 1);
            //对行上的节点对应的列进行恢复
            for(int j = L[i];j != i;j = L[j])
                resume(col[j]);
        }
        //恢复节点数最少列
        resume(c);
        return false;
    }

    //重复覆盖
    //将列与矩阵完全分开
    void Remove1(int c)
    {
        for(int i = D[c];i != c;i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }
    void resume1(int c)
    {
        for(int i = D[c];i != c;i = D[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    int vis[MNN];
    //估价函数,模拟删除列,H(),函数返回的是至少还需要多少行才能完成重复覆盖
    int A()
    {
        int dis = 0;
        for(int i = R[0];i != 0;i = R[i]) vis[i] = 0;
        for(int i = R[0];i != 0;i = R[i]){
            if(!vis[i]){
                dis++;vis[i] = 1;
                for(int j = D[i];j != i;j = D[j]){
                    for(int k = R[j];k != j;k = R[k]){
                        vis[col[k]] = 1;
                    }
                }
            }
        }
        return dis;
    }
    
    void dfs(int deep)
    {
        if(!R[0]){
            //cout << res << endl;
            res = min(res,deep);
            return ;
        }
        if(deep + A() >= res) return ;
        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]){
            //每次将第i列其他节点删除,只保留第i节点,为了找该行的节点
            Remove1(i);
            //将列上的节点完全与矩阵脱离,只删列首节点是不行的
            for(int j = R[i];j != i;j = R[j]){
                Remove1(j);
            }
            dfs(deep + 1);
            for(int j = L[i];j != i;j = L[j]){
                resume1(j);
            }
            resume1(i);
        }
    }
}dlx;

typedef struct Node{
    double x,y;
}Node;
//a存储城市,b存储雷达站
Node a[60],b[60];

double dis(int i,int j)
{
    return (a[i].x - b[j].x) * (a[i].x - b[j].x) + (a[i].y - b[j].y) * (a[i].y - b[j].y);
}

int n,m,k;

bool change(double R)
{
    dlx.init(m,n);
    double ans = R * R;
    for(int i = 1;i <= m;++i){
        for(int j = 1;j <= n;++j){
            if(dis(j,i) <= ans){
                dlx.Insert(i,j);
                //cout << i << " " << j << endl;
            }
        }
    }
    dlx.res = inf;
    dlx.dfs(0);
    if(dlx.res > k){
        return false;
    }else{
        return true;
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d %d",&n,&m,&k);
        for(int i = 1;i <= n;++i){
            scanf("%lf %lf",&a[i].x,&a[i].y);
        }
        for(int i = 1;i <= m;++i){
            scanf("%lf %lf",&b[i].x,&b[i].y);
        }
        double l = 0,r = 10000.0;
        while(r - l > eps){
            double mid = (l + r) / 2.0;
            //cout << mid << endl;
            if(change(mid)) r = mid;
            else l = mid;
        }
        printf("%.6lf\n",r);
    }
    return 0;
}

IDA* + 重复覆盖可能更快点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const double eps = 1e-8;

//最大行数
const int MN = 1005;
//最大列数
const int MM = 1005;
//最大点数
const int MNN = 1e5 + 5 + MM;

struct DLX
{
    //一共n行m列,s个节点
    int n,m,s;
    //交叉十字链表组成部分
    //第i个节点的上U下D左L右R,所在位置row行col列
    int U[MNN],D[MNN],L[MNN],R[MNN],row[MNN],col[MNN];
    //H数组记录行选择指针,S数组记录覆盖个数
    int H[MN],S[MM];
    //res记录行个数,ans数组记录可行解
    int res,ans[MN];
    //初始化空表
    void init(int x,int y)
    {
        n = x,m = y;
        //其中0节点作为head节点,其他作为列首节点
        for(int i = 0;i <= m;++i){
            U[i] = D[i] = i;
            L[i] = i - 1;
            R[i] = i + 1;
        }
        R[m] = 0;L[0] = m;
        s = m;
        memset(S,0,sizeof(S));
        memset(H,-1,sizeof(H));
    }
    void Insert(int r,int c)
    {
        //节点数加一,设置s节点所处位置,以及S列覆盖个数加一
        s++;row[s] = r;col[s] = c;S[c]++;
        //将s节点插入对应列中
        D[s] = D[c];U[D[c]] = s;
        U[s] = c;D[c] = s;
        if(H[r] < 0){//如果该行没有元素,H[r]标记该行起始节点
            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;
        }
    }
    //精确覆盖
    void Remove(int c)
    {
        //删除c列
        L[R[c]] = L[c];R[L[c]] = R[c];
        //删除该列上的元素对应的行
        for(int i = D[c];i != c;i = D[i]){//枚举该列元素
            for(int j = R[i];j != i;j = R[j]){//枚举列的某个元素所在行遍历
                U[D[j]] = U[j];
                D[U[j]] = D[j];
                //将该列上的S数组减一
                --S[col[j]];
            }
        }
    }
    void resume(int c)
    {
        //恢复c列
        for(int i = U[c];i != c;i = U[i]){//枚举该列元素
            for(int j = L[i];j != i;j = L[j]){
                U[D[j]] = j;D[U[j]] = j;
                ++S[col[j]];
            }
        }
        L[R[c]] = c;R[L[c]] = c;
    }
    bool dance(int deep)
    {
        if(res < deep) return false;
        //当矩阵为空时,说明找到一个可行解,算法终止
        if(R[0] == 0){
            res = min(res,deep);
            return true;
        }
        //找到节点数最少的列,枚举这列上的所有行
        int c = R[0];
        for(int i = R[0];i != 0;i = R[i]){
            if(S[i] < S[c]){
                c = i;
            }
        }
        //删除节点数最少的列
        Remove(c);
        for(int i = D[c];i != c;i = D[i]){
            //将行r放入当前解
            ans[deep] = row[i];
            //行上节点对应的列上进行删除
            for(int j = R[i];j != i;j = R[j])
                Remove(col[j]);
            //进入下一层
            dance(deep + 1);
            //对行上的节点对应的列进行恢复
            for(int j = L[i];j != i;j = L[j])
                resume(col[j]);
        }
        //恢复节点数最少列
        resume(c);
        return false;
    }

    //重复覆盖
    //将列与矩阵完全分开
    void Remove1(int c)
    {
        for(int i = D[c];i != c;i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }
    void resume1(int c)
    {
        for(int i = D[c];i != c;i = D[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    int vis[MNN];
    //估价函数,模拟删除列,H(),函数返回的是至少还需要多少行才能完成重复覆盖
    int A()
    {
        int dis = 0;
        for(int i = R[0];i != 0;i = R[i]) vis[i] = 0;
        for(int i = R[0];i != 0;i = R[i]){
            if(!vis[i]){
                dis++;vis[i] = 1;
                for(int j = D[i];j != i;j = D[j]){
                    for(int k = R[j];k != j;k = R[k]){
                        vis[col[k]] = 1;
                    }
                }
            }
        }
        return dis;
    }

    bool dfs(int deep)
    {
        if(deep + A() > res) return false;
        if(!R[0]) return true;
        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]){
            //每次将第i列其他节点删除,只保留第i节点,为了找该行的节点
            Remove1(i);
            //将列上的节点完全与矩阵脱离,只删列首节点是不行的
            for(int j = R[i];j != i;j = R[j]){
                Remove1(j);
            }
            if(dfs(deep + 1)) return true;
            for(int j = L[i];j != i;j = L[j]){
                resume1(j);
            }
            resume1(i);
        }
        return false;
    }
    int IDEA(int k)
    {
        res = 0;
        while(true)
        {
            if(res > k) break;
            //cout << res << endl;
            if(dfs(0)) return res;
            res++;
        }
        return -1;
    }
}dlx;

//01矩阵的行是雷达站,列是城市
typedef struct Node{
    double x,y;
}Node;
//a存储城市,b存储雷达站
Node a[60],b[60];

double dis(int i,int j)
{
    return (a[i].x - b[j].x) * (a[i].x - b[j].x) + (a[i].y - b[j].y) * (a[i].y - b[j].y);
}

int n,m,k;

bool change(double R)
{
    dlx.init(m,n);
    double ans = R * R;
    for(int i = 1;i <= m;++i){
        for(int j = 1;j <= n;++j){
            if(dis(j,i) <= ans){
                dlx.Insert(i,j);
               // cout << i << " " << j << endl;
            }
        }
    }
    int num = dlx.IDEA(k);
    //cout << num << endl;
    if(num > k || num == -1){
        return false;
    }else{
        return true;
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d %d",&n,&m,&k);
        for(int i = 1;i <= n;++i){
            scanf("%lf %lf",&a[i].x,&a[i].y);
        }
        for(int i = 1;i <= m;++i){
            scanf("%lf %lf",&b[i].x,&b[i].y);
        }
        double l = 0,r = 10000.0;
        while(r - l > eps){
            double mid = (l + r) / 2.0;
           // cout << mid << endl;
            if(change(mid)) r = mid;
            else l = mid;
        }
        printf("%.6lf\n",r);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/82925473