[HDU3622]Bomb Game(2-SAT)

8 篇文章 0 订阅

题目描述

传送门

题解

最小值最大,二分答案(实数)
判定?
二分出答案mid之后相当于是有一些点不能同时选(距离 < <script type="math/tex" id="MathJax-Element-1"><</script>mid)
同时每一组两个点必须选一个
就是一个经典的2-SAT问题
我们只需要判断是否有解即可
假设 A1,B2 不能同时选,那么如果选 A1 的话, B1 是必选的
同样地,如果选 B2 ,那么 A2 必选
那么连边 A1 -> B1 , B2 -> A2
用tarjan求强连通分量
如果某一组的两个点在同一个强连通分量里,则无解
因为p能到达的点是选p则必选的点

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 505

const double eps=1e-9;
int dcmp(double x)
{
    if (x<=eps&&x>=-eps) return 0;
    return (x>0)?1:-1;
}
struct Point
{
    double x,y;
    Point (double X=0,double Y=0)
    {
        x=X,y=Y;
    }
};
int n,cnt,dfs_clock,top,scc;
double x,y,ans;
Point p[N*2];
int tot,point[N*2],nxt[N*N*10],v[N*N*10];
int dfn[N*2],low[N*2],vis[N*2],stack[N*2],belong[N*2];

double Dis(Point P,Point Q)
{
    double x=P.x-Q.x,y=P.y-Q.y;
    return sqrt(x*x+y*y);
}
void add(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++dfs_clock;stack[++top]=x;vis[x]=1;
    for (int i=point[x];i;i=nxt[i])
        if (!dfn[v[i]])
        {
            tarjan(v[i]);
            low[x]=min(low[x],low[v[i]]);
        }
        else if (vis[v[i]])
            low[x]=min(low[x],dfn[v[i]]);
    if (dfn[x]==low[x])
    {
        int now=0;++scc;
        while (now!=x)
        {
            now=stack[top--];
            belong[now]=scc;
            vis[now]=0;
        }
    }
}
bool check(double mid)
{
    tot=0;memset(point,0,sizeof(point));
    for (int i=0;i<=cnt;++i)
        for (int j=i+1;j<=cnt;++j)
            if (dcmp(Dis(p[i],p[j])-mid)<0)
            {
                add(i+1,(j^1)+1);
                add(j+1,(i^1)+1);
            }
    memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
    memset(belong,0,sizeof(belong));dfs_clock=top=scc=0;
    for (int i=1;i<=cnt+1;++i)
        if (!dfn[i]) tarjan(i);
    for (int i=0;i<=cnt;i+=2)
        if (belong[i+1]==belong[(i^1)+1])
            return false;
    return true;
}
double find()
{
    double l=0.0,r=20000.0,mid,ans=0.0;
    while (dcmp(r-l)!=0)
    {
        mid=(l+r)/2;
        if (check(mid)) ans=l=mid;
        else r=mid;
    }
    return ans;
}

int main()
{
    while (~scanf("%d",&n))
    {
        cnt=-1;
        for (int i=1;i<=n;++i)
        {
            scanf("%lf%lf",&x,&y);
            p[++cnt]=Point(x,y);
            scanf("%lf%lf",&x,&y);
            p[++cnt]=Point(x,y);
        }
        ans=find();
        printf("%.2lf\n",ans/2);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值