HDU 3156 Repair Depots DLX 计算几何 二分 暴力

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=3156

题意:

n(1<=n<=16) 个点,点的坐标为 x,y(0<=x,y<=10) ,且x, y是实数,给m个维修点,坐标不限,求一个最小距离,使得n个点到m个维修点的距离至少有一个小于等于这个最小距离

思路:

自己不会,看的是这一篇题解:http://www.cnblogs.com/183zyz/archive/2011/08/09/2132767.html,明白了思路自己想了想,的确数据范围有点小,是应该用暴力的方法来求解
二分答案 + 计算几何

  • 对当前二分数值mid的check:
    先枚举所有点,看这些点作为圆心,能不能包含其它的点,用状压表示这些点集,储存起来。
    接着枚举所有点对,检查这个点对是否可以在mid构成半径的圆上(注意,如果这两个点对可以同时在两个圆上,要作为两种情况分开考虑),如果可以,再看这个圆能不能包含其他的点,用状压表示这些点集,储存起来。
    枚举结束后,对状态排序,从高到低枚举每个状态(点集),并消去其子集的标记,如果一个点集有标记,说明这几个点中可以有一个维修点,用addlink()函数标记一下
    然后用dance()爆搜检查答案

  • 已知两个点,一个半径,计算圆心
    一开始还因为精度误差不敢用高中方法,后来发现用高中的思路去求就ok了
    口述一下:
    得到a, b两点的向量,求其法向量,化为单位向量,乘上a,b两点的中点到圆心的距离(用勾股定理),然后a,b两点的中点上下平移那么一个向量就好了

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

#define MS(x, y) memset(x, y, sizeof(x))

typedef long long LL;
const int MAXN = 20;
const int MAXM = 500;
const double EPS = 1e-7;

struct Node {
    double x, y;
};

Node node[MAXN];
int bit[MAXN];
int U[MAXM], D[MAXM], R[MAXM], L[MAXM], Row[MAXM], Col[MAXM];
bool used[MAXN];
int Head[140000], siz[MAXN];
bool vis[140000];
int hsh[140000], hshnum;
int col, tot, limit;
int n;

double dis(Node a, Node b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

Node Circle(Node a, Node b, double r) {
    Node temp, ret;
    temp.x = -1 * (a.y - b.y);
    temp.y = a.x - b.x;
    double len = sqrt(temp.x * temp.x + temp.y * temp.y);
    temp.x /= len;
    temp.y /= len;
    double dist = sqrt(r * r - dis(a, b) * dis(a, b) / 4);
    if (r < 0) dist = -dist;
    ret.x = (a.x + b.x) / 2 + dist * temp.x;
    ret.y = (a.y + b.y) / 2 + dist * temp.y;
    return ret;
}

void init(int _row, int _col) {
    col = _col;
    for (int i = 0; i <= col; ++i) {
        siz[i] = 0;
        U[i] = D[i] = i;
        L[i] = i - 1;
        R[i] = i + 1;
    }
    R[col] = 0; L[0] = col;
    tot = col;
    for (int i = 1; i <= _row; ++i) Head[i] = -1;
}

void addlink(int r, int c) {
    ++siz[Col[++tot] = c];
    Row[tot] = r;
    D[tot] = D[c];
    U[D[c]] = tot;
    U[tot] = c;
    D[c] = tot;
    if (Head[r] < 0) Head[r] = L[tot] = R[tot] = tot;
    else {
        R[tot] = R[Head[r]];
        L[R[Head[r]]] = tot;
        L[tot] = Head[r];
        R[Head[r]] = tot;
    }
}

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;
}

int left_col() {
    int ret = 0;
    for (int c = R[0]; c != 0; c = R[c]) used[c] = false;
    for (int c = R[0]; c != 0; c = R[c]) if (!used[c]) {
        ++ret;
        used[c] = true;
        for (int i = D[c]; i != c; i = D[i])
            for (int j = R[i]; j != i; j = R[j])
                used[Col[j]] = true;
    }
    return ret;
}

bool dance(int dep) {
    if (dep + left_col() > limit) return false;
    if (R[0] == 0) return dep <= limit;
    int c = R[0];
    for (int i = R[0]; i != 0; i = R[i]) if (siz[i] < siz[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(dep + 1)) return true;
        for (int j = L[i]; j != i; j = L[j]) Resume(j);
        Resume(i);
    }
    return false;
}

bool check(double mid) {
//    cout<<mid<<endl;
    init(1 << n, n);
    Node temp1, temp2;
    int now;
    MS(vis, false);
    hshnum = 0;
    for (int i = 0; i < n; ++i) {
        now = 0;
        for (int j = 0; j < n; ++j) if (dis(node[i], node[j]) - mid < EPS) {
            now += bit[j];
        }
        if (!vis[now]) {
            vis[now] = true;
            hsh[hshnum++] = now;
        }
    }
    for (int i = 0; i < n; ++i) {
        for (int j = i + 1; j < n; ++j) if (dis(node[i], node[j]) - 2 * mid < EPS) {
            temp1 = Circle(node[i], node[j], mid);
            temp2 = Circle(node[i], node[j], -mid);
            now = (bit[i] | bit[j]);
            for (int k = 0; k < n; ++k) if (i != k && j != k) {
                if (dis(node[k], temp1) - mid < EPS) {
                    now |= bit[k];
                }
            }
            if (!vis[now]) hsh[hshnum++] = now;
            vis[now] = true;
            if (dis(temp1, temp2) > EPS) {
                now = (bit[i] | bit[j]);
                for (int k = 0; k < n; ++k) if (i != k && j != k) {
                    if (dis(node[k], temp2) - mid < EPS) {
                        now |= bit[k];
                    }
                }
                if (!vis[now]) hsh[hshnum++] = now;
                vis[now] = true;
            }
        }
    }
    sort(hsh, hsh + hshnum);
    for (int i = hshnum - 1; i >= 0; --i) {
        for (int j = 0; j < n; ++j) if ((hsh[i] & bit[j])) {
            vis[(hsh[i] ^ bit[j])] = false;
        }
        if (!vis[hsh[i]]) continue;
        vis[hsh[i]] = false;
        for (int j = 0; j < n; ++j) if ((hsh[i] & bit[j])) {
            addlink(hsh[i], j + 1);
        }
    }
    return dance(0);
}

int main() {
    bit[0] = 1;
    for (int i = 1; i < MAXN; ++i) bit[i] = bit[i - 1] << 1;
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &limit);
        for (int i = 0; i < n; ++i) scanf("%lf%lf", &node[i].x, &node[i].y);
        double l = 0, r = 7.1, mid, ans;
        while (l - r < EPS) {
            mid = (l + r) / 2;
            if (check(mid)) {
                ans = mid;
                r = mid - EPS;
            } else l = mid + EPS;
        }
        printf("%.6f\n", ans + EPS);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值