题目链接:
点击打开链接
题目大意:
有n个炸弹,每个炸弹的放置位置有两个可选,每个炸弹的爆炸范围不能交叉,问我所有炸弹的中爆炸范围最小的那个炸弹的爆炸范围最大是多少
题目分析:
首先炸弹放置的位置是两个,那么就是2-sat问题,我们只需要二分答案,然后判断答案是否合法,如果答案合法,那么答案就可能是更大的值,每次二分出的mid值作为所有炸弹的爆炸范围,如果他们能够得到不会交叉的解,那么说明当前方案可行,建边的时候就是对于任意两个点,枚举他们各自的两个点是否会相交,如果会,那么选其中一个就一定不选另一个
代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <stack>
#include <cmath>
#define MAX 207
#define eps 1e-9
using namespace std;
int mark[MAX],dfn[MAX],low[MAX],belong[MAX],times,cnt;
vector<int> e[MAX];
stack<int> s;
struct Point
{
double x,y;
}p[MAX];
double dis ( Point a , Point b )
{
return sqrt ((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
void tarjan ( int u )
{
dfn[u] = low[u] = ++times;
mark[u] = 1;
s.push ( u );
int len = e[u].size();
for ( int i= 0 ; i < len ; i++ )
{
int v = e[u][i];
if ( !mark[v] )
{
tarjan ( v );
low[u] = min ( low[u] , low[v] );
}
if ( mark[v] == 1 )
low[u] = min ( low[u] , dfn[v] );
}
if ( dfn[u] == low[u] )
{
int temp;
do
{
temp = s.top();
belong[temp] = cnt;
mark[temp] = 2;
s.pop();
}while ( temp != u );
cnt++;
}
}
void init ( )
{
memset ( mark , 0 , sizeof ( mark ));
for ( int i = 0 ; i < MAX ; i++ )
e[i].clear();
while ( !s.empty() ) s.pop();
times = cnt = 0;
}
int n,m;
bool check ( double mid )
{
init();
for ( int i = 0 ; i < n ; i++ )
for ( int j = i+1 ; j < n ; j++ )
{
int x = i<<1 , y = j<<1;
int u = i<<1|1 , v = j<<1|1;
/*if ( dis( p[x] , p[y] ) + eps < mid && dis ( p[u] , p[v] ) + eps < mid
dis( p[u] , p[y] ) + eps < mid && dis ( p[x] , p[v] ) + eps < mid )
return false;*/
if ( dis ( p[x] , p[y] ) + eps < mid*2 )
{
e[x].push_back( v );
e[y].push_back( u );
}
if ( dis ( p[u] , p[v] ) + eps < mid*2 )
{
e[u].push_back ( y );
e[v].push_back ( x );
}
if ( dis ( p[x] , p[v]) + eps < mid*2 )
{
e[x].push_back ( y );
e[v].push_back ( u );
}
if ( dis ( p[u] , p[y] ) + eps < mid*2 )
{
e[u].push_back ( v );
e[y].push_back ( x );
}
}
for ( int i = 0 ; i < 2*n ; i++ )
if ( !mark[i] ) tarjan ( i );
for ( int i = 0 ; i < n ; i++ )
if ( belong[i<<1] == belong[i<<1|1] )
return false;
return true;
}
int main ( )
{
while ( ~scanf ( "%d" , &n ))
{
for ( int i = 0 ; i < n ; i++ )
scanf ( "%lf%lf%lf%lf" , &p[i<<1].x , &p[i<<1].y , &p[i<<1|1].x , &p[i<<1|1].y );
//cout << "YES" << endl;
double l = 0 , r = 10000.0, mid;
int times = 100;
while ( times-- )
{
mid = ( l + r )/2.0;
if ( check ( mid ) ) l = mid;
else r = mid;
}
printf ( "%.2lf\n" , l );
}
}