poj2749 2-SAT判定

【题意】

给出n个牛棚、两个特殊点S1,S2的坐标。S1、S2直连。牛棚只能连S1或S2。还有,某些牛棚只能连在同一个S,某些牛棚不能连在同一个S。求使最长的牛棚间距离最小 距离是曼哈顿距离

【题解】

典型的2-sat判定。条件如下:

x hate y:x->~y,~y->x,~x->y,y->~x

x friend with y: x->y,y->x,~x->~y,~y->~x

二分最大距离limit,则有相应限制:

dist(i,S1)+dist(S1,j)>limit  Xi->~Xj  Xj->Xi

dist(i,S2)+dist(S2,j)>limit  ~Xi->Xj  ~Xj->Xi

dist(i,S1)+dist(S1,S2)+dist(S2,j)>limit Xi->Xj  ~Xj->~Xi

dist(i,S2)+dist(S2,S1)+dist(S1,j)>limit ~Xi->~Xj  Xj->Xi

【注意点】

对于2-sat判定,建图时逻辑要清晰,否则就会建错边。

【代码】

#include <iostream>
using namespace std;
const int maxn=1010;  
const int maxe=maxn*maxn*2;  

struct edge  
{  
       int x,y,next;  
}e[maxe];  
int dfn[maxn],low[maxn],v[maxn],s[maxn],b[maxn],h[maxn]; 
int xa[maxn],xb[maxn],ya[maxn],yb[maxn],x[maxn],y[maxn],d[maxn]; 
int n,tot,cnt,ans,times,t,x1,x2,y1,y2,na,nb,ll,rr,mid,D; 
 
void ins(int x,int y)  
{  
     e[++tot].x=x;e[tot].y=y;  
     e[tot].next=h[x];h[x]=tot;  
}  
  
void tarjan(int x)  
{  
     int y,i;  
     times++;t++;  
     dfn[x]=low[x]=times;  
     v[x]=1;s[t]=x;  
     for (i=h[x];i;i=e[i].next)  
     {  
         y=e[i].y;  
         if (v[y]==0)  
         {  
            tarjan(y);  
            low[x]=min(low[x],low[y]);  
         }  
         if (v[y]==1)  
            low[x]=min(low[x],dfn[y]);  
     }  
     if (dfn[x]==low[x])  
     {  
        cnt++;  
        do  
        {  
              y=s[t--];  
              b[y]=cnt;v[y]=2;  
        }while (y!=x);  
     }  
} 

void build(int mid)
{
     tot=0;
     memset(h,0,sizeof(h));
     int i,j;
     for (i=1;i<=na;i++)
     {
         ins(xa[i],ya[i]+n);
         ins(ya[i]+n,xa[i]);
         ins(xa[i]+n,ya[i]);
         ins(ya[i],xa[i]+n);
     }
     for (i=1;i<=nb;i++)
     {
         ins(xb[i],yb[i]);
         ins(yb[i],xb[i]);
         ins(xb[i]+n,yb[i]+n);
         ins(yb[i]+n,xb[i]+n);
     }
     for (i=1;i<=n;i++)
         for (j=i+1;j<=n;j++)
         {
             if (d[i]+d[j]>mid)
             {
                ins(i,j+n);
                ins(j,i+n);
             }
             if (d[i+n]+d[j+n]>mid)
             {
                ins(i+n,j);
                ins(j+n,i);
             }
             if (d[i]+d[j+n]+D>mid)
             {
                ins(i,j);
                ins(j+n,i+n);
             }
             if (d[i+n]+d[j]+D>mid)
             {
                ins(i+n,j+n);
                ins(j,i);
             }
         }
}

bool ok()
{
     int i;
     memset(v,0,sizeof(v));
     memset(dfn,0,sizeof(dfn));
     memset(low,0,sizeof(low));
     memset(b,0,sizeof(b));
     times=0;t=0;cnt=0;
     for (i=1;i<=n;i++)
         if (v[i]==0)
            tarjan(i);
     for (i=1;i<=n;i++)
         if (b[i]==b[i+n])
            return false;
     return true;
}

int main()
{
    freopen("pin.txt","r",stdin);  
    freopen("pou.txt","w",stdout); 
    int i;
    cin >> n >> na >> nb;
    cin >> x1 >> y1 >> x2 >> y2;
    for (i=1;i<=n;i++)
        cin >> x[i] >> y[i];
    for (i=1;i<=na;i++)
        cin >> xa[i] >> ya[i];
    for (i=1;i<=nb;i++)
        cin >> xb[i] >> yb[i];
    for (i=1;i<=n;i++)
    {
        d[i]=abs(x[i]-x1)+abs(y[i]-y1);
        d[i+n]=abs(x[i]-x2)+abs(y[i]-y2);
    }
    D=abs(x1-x2)+abs(y1-y2);
    ans=-1;
    ll=1;rr=4000000;
    while (ll<=rr)
    {
          mid=(ll+rr)/2;
          build(mid);
          if (ok())
             ans=mid,rr=mid-1;
          else
              ll=mid+1;
    }
    cout << ans << endl;
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值