题意:有一个有趣的游戏,每一轮给定两个点,只能且必须在一个点上面放置炸弹,炸弹的爆炸范围是任意的,但是任意两个炸弹的爆炸范围不能相交,在n轮游戏过后,得分为最小爆炸范围的半径,求n轮游戏之后的最大可能得分。
题目为每次只能在两个里选一个,是2-SAT模型,又所求的为最大化最小值,所以用二分。
每次看两点间距离是否大于半径的二倍,是就可以都选,跑2-SAT,如果一组里的值都选了,则说明半径小了,右移,否则左移。
链接:hdu - 3622
代码:
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 1e5 + 7;
const double eps = 1e-5;
int x[MAXN], y[MAXN];
struct node{
int v, next;
} edge[MAXN];
int head[MAXN], index;
void add_edge(int u, int v) {
edge[index].v = v;
edge[index].next = head[u];
head[u] = index++;
}
int DFN[MAXN], low[MAXN], stack_[MAXN], in_stack[MAXN], belong[MAXN];
int cir, top, temp;
void Tarjan(int u) {
int p;
DFN[u] = low[u] = ++temp;
in_stack[u] = 1;
stack_[top++] = u;
for(int i = head[u]; i + 1; i = edge[i].next) {
int v = edge[i].v;
if(!DFN[v]) {
Tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(in_stack[v])
low[u] = min(low[u], DFN[v]);
}
if(low[u] == DFN[u]) {
++cir;
do {
p = stack_[--top];
in_stack[p] = 0;
belong[p] = cir;
}
while(p != u);
}
}
double dist(int i, int j) {
return sqrt((double)(x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
}
int main()
{
int n, m;
while(scanf("%d", &n) != EOF) {
for(int i = 0; i < n << 1; ++i)
scanf("%d %d", &x[i], &y[i]);
double ans;
double l = 0.0, r = 40000.0, mid;
while(r - l >= eps) {
mid = (l + r) / 2.0;
temp = cir = top = index = 0;
memset(DFN, 0, sizeof(DFN));
memset(head, -1, sizeof(head));
memset(in_stack, 0, sizeof(in_stack));
memset(belong, 0, sizeof(belong));
for(int i = 0; i < n << 1; ++i)
for(int j = i + 1; j < n << 1; ++j) {
if(j - i == 1 && i & 1 == 0) continue;
if(dist(i, j) < mid * 2.0) {
add_edge(j ^ 1, i);
add_edge(i ^ 1, j);
}
}
for(int i = 0; i < n << 1; ++i)
if(!DFN[i]) Tarjan(i);
int flag = 0;
for(int i = 0; i < n; ++i)
if(belong[i << 1] == belong[(i << 1) ^ 1]) {
flag = 1;
break;
}
if(!flag) {
ans = mid;
l = mid;
}
else r = mid;
}
printf("%.2lf\n", ans);
}
return 0;
}