hdu 3622 2-sat经典问题

 
/*
典型的2-sat题目
二分半径
两点x,y有冲突,就把x连一条到yy(即y的对立点),y连一条到xx
然后求强联通分量,如果存在x到xx属于一个联通分量,则不符合条件
*/
#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<cmath>  
#include<queue>  
#include<stack>  
#include<map>  
#include<algorithm>  
using namespace std;  
const int MAXE=1000002;  
const int MAX=202;  
const double eps=1e-8;
struct point 
{
    double x,y;
}p[MAX];
double dis(point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double Dis[MAX][MAX];
double L,R;
struct EDGE    
    {    
        int v; // 从u点出发能到达的点v    
        int next; // 从u点出发能到达的下一条边的编号    
    }edge[MAXE];    
    int first[MAX];   // first[u] = e:从点u出发的最后一条边的编号是e(“最后”是指最后输入)    
    int dfn[MAX];  // dfn[u]:节点u搜索的次序编号(时间戳)    
    int low[MAX];// low[u]:是u或u的子树能够追溯到的最早的栈中节点的次序号    
    int ins[MAX];// 是否在栈中    
    int scc[MAX]; // scc[i] = j:第i个点所在的强连通分量的编号  (此题可有可无)  
    int n;    
    int num; // 强连通分量的数目    
    int index; // 次序编号    
    int s[MAX];    
    int top,e;
    int DFS(int x)    
    {    
        low[x]=dfn[x]=index++;    
        s[++top]=x;    
        ins[x]=1;    
        // 枚举每一条边:u-->v    
        for(int k=first[x];k!=-1;k=edge[k].next)    
        {    
            int v=edge[k].v;    
            if(dfn[v]==0)    
            {    
   
                DFS(v);    
                low[x]=min(low[x],low[v]);   
            }    
            else if(ins[v])    
            {    
                low[x]=min(dfn[v],low[x]);   
 
            }    
        }    
         // 如果节点u是强连通分量的根    
        if(low[x]==dfn[x])    
        {    
            int v;    
            num++;    
            do{    
            v=s[top--];     
            ins[v]=0;    
            scc[v]=num;    
            }while(v!=x);    
        }    
        return 1;  
    }    
    
 
void init()
{
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf%lf%lf",&p[i].x,&p[i].y,&p[i+n].x,&p[i+n].y);
    }
    L=(1LL)<<36;
    R=0;
    for(int i=1;i<=2*n;i++)
    for(int j=i+1;j<=2*n;j++)
    {
        Dis[i][j]=Dis[j][i]=dis(p[i],p[j]);
        if(Dis[i][j]>R)
        R=Dis[i][j];
        if(Dis[i][j]<L)
        L=Dis[i][j];
    }
}
void add(int u,int v)
{
    edge[e].v=v;
    edge[e].next=first[u];
    first[u]=e++;
}
bool check()
{
    for(int i=1;i<=n;i++)
    if(scc[i]==scc[i+n])
    return false;
    return true;
}
void solve()  
{  
    double mid,ii,jj;
    R=R/2.0;
    L=L/2.0;
    //cout<<L<<" "<<R<<endl;
    while(R-L>eps)
    {
            memset(dfn,0,sizeof(dfn));    
            memset(low,0,sizeof(low));    
            memset(first,-1,sizeof(first));    
            memset(ins,0,sizeof(ins));     
            index=1;  
            top=-1;
            num=0;    
            e=0;
            mid=(L+R)/2.0;
            for(int i=1;i<=2*n;i++)
            {
                for(int j=i+1;j<=2*n;j++)
                if(Dis[i][j]<2*mid)
                {
                    if(i<=n)ii=i+n;
                    else ii=i-n;
                    if(j<=n)jj=j+n;
                    else jj=j-n;
                    add(i,jj);
                    //add(jj,i);
                //    add(ii,j);
                    add(j,ii);
                //    cout<<i<<" "<<jj<<endl;
                //    cout<<j<<" "<<ii<<endl;
                }
            }
        for(int i=1;i<=2*n;i++)  
        {  
            if(dfn[i]==0)  
            {  
            DFS(i);  
            }  
        }  
    //    cout<<num<<endl;
        if(check())L=mid+eps;
        else R=mid-eps;
    }
    printf("%.2lf\n",L);
}  
int main()  
{  
    while(scanf("%d",&n)!=EOF)
    {
        init();
        solve();
    }
    return 0;  
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值