题目链接:
点击打开链接
题目大意:
有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 ); } }