[hdu-3622] Bomb Game题解

题目传送门
题意解析:这题就是给了你n组点,每组有两个点,每个点都有一个坐标,然后每组中选择一个点,然后给以你选择的点为圆心的点找共同的一个半径,使每两个圆没有公共区域(也就是互不相交),让你找出最大的半径(hdu上说是最小半径,应该是题目出问题了)。


My opinion:因为当时刚刚讲完2-sat问题,做的题就呵呵了。而且这每两个点去一个点,还有约束条件,实在太明显了。但是在你不知道半径的情况下该如何判断这两个点是否矛盾呢?有句话说得好,有条件要上,没有条件创造条件也要上。于是我们就去创造条件,因为坐标大小在[-10000,10000],所以在极限的情况下,半径大小为绝对不到40000(反正n才100,二分还是lg级的,草率点没关系)。
至于如何判断两个圆是否矛盾,只要两个圆的圆心之间的距离大于半径之和就有公共区域。
总结:
1、输入后二分答案。
2、对于每个mid,建一次图。
3、对于每次的图做一遍2-sat
(对了,因为要保留两位小数,所以精度很有问题,eps=1e-5够了,更大也可以,总之不能1e-3以上)


贼长的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath> 
#include<vector>
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define Clear(a,x) memset(a,x,sizeof(a))
#define sqr(x) (x)*(x)
#define ll long long
#define db double
#define INF 2000000000
#define eps 1e-5
using namespace std;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') f=ch=='-'?-1:f,ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=205,maxm=40005;
struct edge{
    int next,v;
}E_1[maxm],E_2[maxm];
struct node{
    int x,y;
}a[maxn<<1];
int head_1[maxn],head_2[maxn];
bool flag1[maxn],flag2[maxn];
int c[maxn],belong[maxn];
int len,num1,num2,n;
void init(){
    Clear(head_1,-1);
    Clear(head_2,-1);
    Clear(flag1,0);
    Clear(flag2,0);
    len=0;
    num1=num2=0;
}
db calc(node a1,node a2){
    return sqrt((db)sqr(a1.x-a2.x)+(db)sqr(a1.y-a2.y));
}
void add(int u,int v){
    E_1[++len].v=v;
    E_1[len].next=head_1[u];
    head_1[u]=len;
    E_2[len].v=u;
    E_2[len].next=head_2[v];
    head_2[v]=len;
}
void solve(db mid){
    rep(i,0,n*2-3){
        int t;
        if (i%2==0) t=i+2;
            else t=i+1;
        rep(j,t,2*n-1)
            if (calc(a[i],a[j])<2*mid){
                add(i,j^1);
                add(j,i^1);
            }
    }
}
void dfs1(int u){
    flag1[u]=1;
    for (int e=head_1[u];e!=-1;e=E_1[e].next){
        int v=E_1[e].v;
        if (!flag1[v])
            dfs1(v);
    }
    c[++num1]=u;
}
void dfs2(int u){
    flag2[u]=1;
    belong[u]=num2;
    for (int e=head_2[u];e!=-1;e=E_2[e].next){
        int v=E_2[e].v;
        if (!flag2[v])
            dfs2(v);
    }
}
bool ok(int n){
    rep(i,0,n*2-1)
        if (!flag1[i])
         dfs1(i);
    per(i,num1,1)
        if (!flag2[c[i]]){
            dfs2(c[i]);
            num2++;        
        }
    for (int i=0;i<=2*n-2;i+=2)
        if (belong[i]==belong[i+1]) return false;
    return true;
}
int main(){
    while (~scanf("%d",&n)){
        rep(i,0,n-1){
            a[i*2].x=read();
            a[i*2].y=read();
            a[i*2+1].x=read();
            a[i*2+1].y=read();
        }
        db l=0,r=maxm;
        while (r-l>=eps){
            db mid=(l+r)/2; 
            init();
            solve(mid);
            if (ok(n)) l=mid;
                else r=mid;
        }
        printf("%.2lf\n",r);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值