您的位置:首页 > 其它

HDU 2295 Radar 重复覆盖 DLX

2014-11-16 21:17 405 查看
题意:

  N个城市,M个雷达站,K个操作员,问雷达的半径至少为多大,才能覆盖所有城市。M个雷达中最多只能有K个同时工作。

思路:

  二分雷达的半径,看每个雷达可以覆盖哪些城市,然后做重复覆盖,判断这个半径是否可行。

  我是直接二分的半径,跑了300+ms,看了Virtual Judge上面跑得快的代码,才发现为了不浪费半径的长度,最小的半径一定等于某一个雷达站到某一个城市之间的距离。这样一来枚举量就小了很多,只需要15ms……

代码:

  300+ms的 -_-

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <functional>
#include <cctype>
#include <time.h>

using namespace std;

const double eps = 1e-8;
const int INF = 1<<30;
const int MAXN = 55;
const int MAXR = 55;
const int MAXNODE = MAXN*MAXR;

struct DLX {
// 行编号从1开始,列编号为1~n,结点0是表头结点;结点1~n是各列顶部的虚拟结点
int n, sz; // 列数,结点总数
int S[MAXN]; //各列结点数

int row[MAXNODE], col[MAXNODE]; //各结点行列编号
int L[MAXNODE], R[MAXNODE], U[MAXNODE], D[MAXNODE]; //十字链表

int ansd, ans[MAXR]; // 解

void init(int n)  { //n是列数
this->n = n;

//虚拟结点
for (int i = 0; i <= n; i++) {
U[i] = i; D[i] = i; L[i] = i-1; R[i] = i+1;
}
R
= 0; L[0] = n;
sz = n+1;
memset(S, 0, sizeof(S));
}

void addRow(int r, vector<int> columns) {
//这一行的第一个结点
//行没有设头结点,每一行连成一个环形
int first = sz;
for (int i = 0; i < columns.size(); i++) {
int c = columns[i];
L[sz] = sz-1; R[sz] = sz+1; D[sz] = c; U[sz] = U[c];
D[U[c]] = sz; U[c] = sz;
row[sz] = r; col[sz] = c;
S[c]++; sz++;
}
R[sz-1] = first; L[first] = sz-1;
}

//顺着链表A,遍历s外的其他元素
#define FOR(i, A, s) for (int i = A[s]; i != s; i = A[i])

void remove(int c) { //删除c列
FOR(i, D, c) {
L[R[i]] = L[i]; R[L[i]] = R[i];
}
}

void restore(int c) { //回连c列
FOR(i, U, c) {
L[R[i]] = R[L[i]] = i;
}
}

bool vis[MAXN];

int f() {
int ret = 0;
FOR(c, R, 0) vis[c] = true;
FOR(c, R, 0) if (vis[c]) {
ret++; vis[c] = false;
FOR(i, D, c) FOR(j, R, i)
vis[col[j]] = false;
}
return ret;
}

void dfs(int d) { //d为递归深度
if (d+f()>=ansd) return ;
if (R[0] == 0) { //找到解
ansd = min(ansd, d); //记录解的长度
return ;
}

//找S最小的C列
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所在的行覆盖第c列
remove(i);
for (int j = R[i]; j != i; j = R[j]) remove(j); //删除节结点i所在行覆盖第c列
ans[d] = row[i];
dfs(d+1);
for (int j = L[i]; j != i; j = L[j]) restore(j); // 恢复
restore(i);
}
}

int solve() {
ansd = INF;
dfs(0);
return ansd;
}
};

DLX solver;
vector<int> columns;

int n, m, k;
double a[MAXN][2];
double b[MAXN][2];
double G[MAXN][MAXN];

int check(double x) {
solver.init(n);
for (int i = 0; i < m; i++) {
columns.clear();
for (int j = 0; j < n; j++) if (G[i][j]<=x)
columns.push_back(j+1);
if (!columns.empty()) solver.addRow(i+1, columns);
}
return solver.solve();
}

int main() {
#ifdef Phantom01
freopen("HDU2295.txt", "r", stdin);
#endif //Phantom01

int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i < n; i++)
scanf("%lf%lf", &a[i][0], &a[i][1]);
for (int i = 0; i < m; i++)
scanf("%lf%lf", &b[i][0], &b[i][1]);
double l = 0, r = 0, mm;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
G[i][j] = sqrt((b[i][0]-a[j][0])*(b[i][0]-a[j][0]) + (b[i][1]-a[j][1])*(b[i][1]-a[j][1]));
r = max(r, G[i][j]);
}
}
while (abs(r-l)>eps) {
mm = (l+r)/2;
if (check(mm)>k) l = mm;
else r = mm;
}
printf("%.6f\n", r+eps);
}

return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: