[二分+2-sat]Hdu 3622——Bomb Game

题目传送门

题目概述

在一个无限大的平面里画n个圆。
每个圆有2个待选的放置位置。
限制条件为圆不相交,求最小半径的最大值。

解题思路

显然如果R满足限制条件,那么任意r(r < <script type="math/tex" id="MathJax-Element-1"><</script>R)肯定满足限制条件,想到用二分枚举答案。
接下来考虑如何验证。
不难发现这是一个2-sat问题。
假设位置 i,j 以当前最小半径做圆相交,显然可以得到

i>j
j>i

如果对这句话不理解 点这里
接下来用tarjan刷环,判断 i,i 是否在同一个环上就可了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=205,maxm=40005;
int n,x[maxn],y[maxn],dfn[maxn],low[maxn],h[maxn];
int tot,lnk[maxn],son[maxm],nxt[maxm],top,s[maxn],time,G;
bool ins[maxn];
inline int _read(){
    int num=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num*f;   
}
int sqr(int x){return x*x;}
int getl(int i,int j){return sqr(x[i]-x[j])+sqr(y[i]-y[j]);}
void add(int x,int y){
    nxt[++tot]=lnk[x];lnk[x]=tot;son[tot]=y;
}
void tarjan(int x){
    dfn[x]=++time;low[x]=time;
    s[++top]=x;ins[x]=1;
    for (int j=lnk[x];j;j=nxt[j])
    if (!dfn[son[j]]) {tarjan(son[j]);low[x]=min(low[x],low[son[j]]);}
    else if (ins[son[j]]) low[x]=min(low[x],dfn[son[j]]);
    if (dfn[x]==low[x]){
        G++;
        while (s[top]!=x){h[s[top]]=G;ins[s[top]]=0;top--;}
        h[x]=G;ins[x]=0;top--;
    }
}
bool check(double x){
    memset(lnk,0,sizeof(lnk));tot=0;
    for (int i=0;i<2*n;i++)
    for (int j=0;j<i;j++)
    if ((i/2!=j/2)&&getl(i,j)<(double)4*x*x) add(i,j^1),add(j,i^1);
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    time=0;G=0;
    for (int i=0;i<2*n;i++) if (!dfn[i]) tarjan(i);
    for (int i=0;i<n;i++) if (h[2*i]==h[2*i^1]) return 0;
    return 1;
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    while (scanf("%d",&n)==1){
        for (int i=0;i<n;i++){
            x[2*i]=_read();y[2*i]=_read();
            x[2*i^1]=_read();y[2*i^1]=_read();
        }
        double L=0,ans,R=4e4,mid;
        while (R-L>=1e-5){
            mid=(L+R)/2;
            if (check(mid)){ans=mid;L=mid;}else R=mid;
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值