[POJ3622]-Bomb Game-二分答案+2-SAT

说在前面

久违的1A,开心qvq


题目

POJ3622传送门

题目大意

给定N个点对,在每一个点对中选择一个点画圆,半径自定,要求画的圆不能有并(不存在一个位置同时被多个圆覆盖)
得分是所有圆中半径最小的那个,需要求出最大得分。

输入输出格式

输入格式:
包含多组数据,对于每一组数据:
第一行一个整数N,表示点对数(不超过100)
接下来每行四个整数 x1 , y1 , x2 , y2 描述一个点对

输出格式:
对于每组数据,输出一行一个实数表示答案(保留两位小数)


解法

简单2-sat题
要求每一个点对「选且仅选一个点」,并且选的点之间有限制,这就是一个2-sat模型
二分答案(半径),然后用这个距离去判断冲突,建图跑2-sat
有可行解说明当前可以作为答案,不然就不行


下面是优美的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , C_cnt , oppo[205] , head[205] , tp ;
struct Path{
    int pre , to ;
}p[2*(1+200)*200/2 + 5 ] ;
struct Points{
    int x , y ;
    double dist2( const Points &A ){
        return ( A.x - x ) * ( A.x - x ) + ( A.y - y ) * ( A.y - y ) ;
    }
}C[205] ;

void In( int t1 , int t2 ){
    p[++tp].pre = head[t1] ;
    p[ head[t1] = tp ].to = t2 ;
}

double mid ;
void build(){
    double tmp = 4 * mid * mid ; // mid is radius , half of the distence 
    for( int i = 1 ; i <= C_cnt ; i ++ )
    for( int j = i + 1 ; j <= C_cnt ; j ++ )
        if( C[i].dist2( C[j] ) <= tmp ){
            In( i , oppo[j] ) ;
            In( j , oppo[i] ) ;
        }
}

int sta[205] , topp ;
int dfn[205] , dfs_c , scc[205] , scc_cnt ;
int dfs( int u ){
    sta[++topp] = u ;
    int lowu = dfn[u] = ++dfs_c ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( !dfn[v] ) lowu = min( lowu , dfs( v ) ) ;
        else if( !scc[v] ) lowu = min( lowu , dfn[v] ) ;
    }
    if( lowu == dfn[u] ){
        scc_cnt ++ ;
        while( 1 ){
            int x = sta[topp--] ;
            scc[x] = scc_cnt ;
            if( x == u ) break ;
        }
    } return lowu ;
}

void clear_G(){
    dfs_c = tp = 0 ;
    memset( head , 0 , sizeof( head ) ) ;
    memset( scc , 0 , sizeof( scc ) ) ;
    memset( dfn , 0 , sizeof( dfn ) ) ;
}

bool check(){
    clear_G() ; build() ;
    for( int i = 1 ; i <= C_cnt ; i ++ )
        if( !dfn[i] ) dfs( i ) ;
    for( int i = 1 ; i <= C_cnt ; i ++ ){
        if( scc[i] == scc[ oppo[i] ] ) return false ;
    } return true ;
}

void solve(){
    double lf = 0 , rg = 20000 * 2 , ans ;
    while( lf <= rg - 0.0005 ){
        mid = ( lf + rg ) / 2 ;
        if( check() ){
            ans = mid ;
            lf = mid ;
        } else rg = mid ;
    }
    printf( "%.2f\n" , ans ) ;
}

int main(){
    while( scanf( "%d" , &N ) != EOF ){
        C_cnt = 0 ;
        for( int i = 1 , a1 , b1 , a2 , b2 ; i <= N ; i ++ ){
            scanf( "%d%d%d%d" , &a1 , &b1 , &a2 , &b2 ) ;
            C[++C_cnt] = ( Points ){ a1 , b1 } ;
            C[++C_cnt] = ( Points ){ a2 , b2 } ;
            oppo[C_cnt] = C_cnt - 1 ;
            oppo[C_cnt - 1] = C_cnt ;
        }
        solve() ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值