HDU 3622 —— Bomb Game(2-SAT,二分答案)

6 篇文章 0 订阅
2 篇文章 0 订阅

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3622

题意:有N对点,每对点中必须选择一个点,以每个选择点为中心画圆,每个圆的半径不作要求,但唯一限制的是所有圆不能相交,问画出来的所有圆中半径最小的值,最大是多少?

感觉跟《训练指南》中2-SAT这块的例题很类似。

对答案进行二分,把问题转化为特定半径的可行性判断。假设当前最小半径是mid,那么如果两个点(不是同一对的)的距离小于2*mid,那么说明两圆相交,有冲突,那么就连边到它们各自取反的点。然后就是很常规的2-SAT问题了。

PS:写的时候智商又捉急了。。。每次判断两个点是否冲突都去计算一次距离,TLE了很多下,纠结了很久才发现是这个地方大量的重复计算,而且还是很蛋疼地浮点数平方和开方,后来开了个数组记录这些结果,马上变成400+MS了,看来浮点数的威力还是不容小视。。。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
#define pb push_back
#define N 200
vector<int>  V[N];
const double eps = 1e-5;
double x[N], y[N], d[N][N];
int n;
double dist(int a, int b){
    return sqrt(pow(x[a]-x[b],2.0) + pow(y[a]-y[b],2.0));
}
bool mark[N];
int S[N], c;
bool dfs(int x){
    if(mark[x^1])   return 0;
    if(mark[x]) return 1;
    mark[x]=1;
    S[c++]=x;
    for(int i=0; i<V[x].size(); i++){
        if(!dfs(V[x][i]))   return 0;
    }
    return 1;
}
bool check(){
    memset(mark,0,sizeof(mark));
    for(int i=0; i<n*2; i+=2){
        if(!mark[i] && !mark[i+1]){
            c = 0;
            if(!dfs(i)){
                while(c>0)  mark[S[--c]]=0;
                if(!dfs(i+1))   return 0;
            }
        }
    }
    return 1;
}
void solve(){
    double low=0.0, top=40000.0, mid;
    while(top-low>eps){
        mid = low+top;
        for(int i=0; i<n*2; i++)    V[i].clear();
        for(int i=0; i<n*2; i++){
            for(int j=i+1; j<n*2; j++){
                if((i>>1)==(j>>1))  continue;
                double tmp = d[i][j];
                if(tmp<mid){
                    V[i^1].pb(j);
                    V[j^1].pb(i);
                }
            }
        }
        mid*=0.5;
        if(check())  low=mid;
        else    top=mid;
    }
    printf("%.2lf\n", low);
}
int main(){
    while(~scanf("%d", &n)){
        for(int i=0; i<n; i++){
            scanf("%lf %lf %lf %lf", x+i*2, y+i*2, x+i*2+1, y+i*2+1);
        }
        for(int i=0; i<n*2; i++){
            for(int j=i+1; j<n*2; j++){
                d[i][j] = d[j][i] = dist(i,j);
            }
            d[i][i]=0.0;
        }
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值