题目: Bomb Game
链接: http://acm.hdu.edu.cn/showproblem.php?pid=3622
题意: 对于点对a[i], b[i], 对于每个i求出选出a或b,以选出的点为圆心,r为半径画圆,要求圆两两不相交,求出最大的半径
题解: 二分半径r,对于可能相交的两个点建双边排除,2-sat判断是否有可行解
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 10000
#define M 1000000
using namespace std;
int ind[N], id[N];
int t[M], nt[M];
int dfn[N], low[N];
int s[N];
bool vis[N];
int l;
int n;
int cnt, num, idn;
struct node
{
double x, y;
double dis(node b)
{
return ((b.x - x) * (b.x - x) + (b.y - y) * (b.y - y));
}
}point[N];
void add(int i, int j)
{
cnt++;
t[cnt] = j;
nt[cnt] = ind[i];
ind[i] = cnt;
}
void tarjan(int x)
{
num++;
dfn[x] = low[x]= num;
s[++l] = x;
vis[x] = true;
for(int k = ind[x]; k != -1; k = nt[k])
{
if(dfn[t[k]] == 0) tarjan(t[k]);
if(vis[t[k]]) low[x] = min(low[x], low[t[k]]);
}
if(dfn[x] == low[x])
{
idn++;
while(true)
{
int tmp = s[l--];
id[tmp] = idn;
vis[tmp] = false;
if(tmp == x) break;
}
}
}
bool chk(double r)
{
cnt = 0;
l = 0;
idn = 0;
memset(vis, 0, sizeof vis);
num = 0;
for(int i = 0; i < 4 * n; i++) ind[i] = -1;
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(id, 0, sizeof id);
for(int i = 0; i < 2 * n; i++)
{
for(int j = i + 1; j < 2 * n; j++)
{
if((i ^ 1) != j && point[i].dis(point[j]) < 4.0 * r * r)
{
add(i, j ^ 1);
add(j, i ^ 1);
}
}
}
for(int i = 0; i < 2 * n; i++)
{
if(dfn[i] == 0) tarjan(i);
}
for(int i = 0; i < 2 * n; i++)
{
if(id[i] == id[i ^ 1]) return false;
}
return true;
}
int main()
{
while(~scanf("%d", &n))
{
for(int i = 0; i < n; i++)
{
scanf("%lf%lf%lf%lf", &point[2 * i].x, &point[2 * i].y, &point[2 * i + 1].x, &point[2 * i + 1].y);
}
double l = 0, r = 1e9;
for(int i = 0; i < 100; i++)
{
double mid = (l + r) / 2.0;
if(chk(mid)) l = mid;
else r = mid;
}
if(chk(r)) printf("%.2lf\n", r);
else printf("%.2lf\n", l);
}
return 0;
}