hdu3622(二分&2-sat)

二分答案,若距离冲突(dist【i】【j】<2*mid)这符合2-sat建图条件,然后判可行解就好。

代码来自匡斌

/*
HDU 3622
题意:给n对炸弹可以放置的位置(每个位置为一个二维平面上的点),
每次放置炸弹是时只能选择这一对中的其中一个点,每个炸弹爆炸
的范围半径都一样,控制爆炸的半径使得所有的爆炸范围都不相
交(可以相切),求解这个最大半径.
     首先二分最大半径值,然后2-sat构图判断其可行性,对于每
     两队位置(u,uu)和(v,vv),如果u和v之间的距离小于2*id,也就
     是说位置u和位置v处不能同时防止炸弹(两范围相交),所以连边(u,vv)
     和(v,uu),求解强连通分量判断可行性.


注意精度问题
*/

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<math.h>
using namespace std;
const int MAXN=210;
const int MAXM=40005;//边的最大数
const double eps=1e-5;

struct Edge
{
    int to,next;
}edge1[MAXM],edge2[MAXM];
int head1[MAXN];
int head2[MAXN];
int tol1,tol2;
bool vis1[MAXN],vis2[MAXN];
int Belong[MAXN];//连通分量标记
int T[MAXN];//dfs结点结束时间
int Bcnt,Tcnt;
void add(int a,int b)//原图和逆图都要添加
{
    edge1[tol1].to=b;
    edge1[tol1].next=head1[a];
    head1[a]=tol1++;
    edge2[tol2].to=a;
    edge2[tol2].next=head2[b];
    head2[b]=tol2++;
}
void init()//建图前初始化
{
    memset(head1,-1,sizeof(head1));
    memset(head2,-1,sizeof(head2));
    memset(vis1,false,sizeof(vis1));
    memset(vis2,false,sizeof(vis2));
    tol1=tol2=0;
    Bcnt=Tcnt=0;
}
void dfs1(int x)//对原图进行dfs,算出每个结点的结束时间,哪个点开始无所谓
{
    vis1[x]=true;
    int j;
    for(int j=head1[x];j!=-1;j=edge1[j].next)
      if(!vis1[edge1[j].to])
        dfs1(edge1[j].to);
    T[Tcnt++]=x;
}
void dfs2(int x)
{
    vis2[x]=true;
    Belong[x]=Bcnt;
    int j;
    for(j=head2[x];j!=-1;j=edge2[j].next)
       if(!vis2[edge2[j].to])
         dfs2(edge2[j].to);
}

struct Point
{
    int x,y;
}s[MAXN];
double dist(Point a,Point b)
{
    return sqrt((double)(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

bool ok(int n)//判断可行性
{
    for(int i=0;i<2*n;i++)
      if(!vis1[i])
        dfs1(i);
    for(int i=Tcnt-1;i>=0;i--)
      if(!vis2[T[i]])//这个别写错,是vis2[T[i]]
      {
          dfs2(T[i]);
          Bcnt++;
      }
    for(int i=0;i<=2*n-2;i+=2)
      if(Belong[i]==Belong[i+1])
        return false;
    return true;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    double left,right,mid;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;i++)
          scanf("%d%d%d%d",&s[2*i].x,&s[2*i].y,&s[2*i+1].x,&s[2*i+1].y);
        left=0;
        right=40000.0;
        while(right-left>=eps)
        {
            mid=(left+right)/2;
            init();
            for(int i=0;i<2*n-2;i++)
            {
                int t;
                if(i%2==0)t=i+2;
                else t=i+1;
                for(int j=t;j<2*n;j++)
                   if(dist(s[i],s[j])<2*mid)//冲突了
                   {
                       add(i,j^1);
                       add(j,i^1);//注意顺序不能变的
                   }
            }
            if(ok(n))left=mid;
            else right=mid;
        }
        printf("%.2f\n",right);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值