poj-2749(2-sat+二分)

题目链接:http://poj.org/problem?id=2749

题意:有N个牛棚,牛棚的牛想串门,必须两两牛棚互通,吝啬的农场主不愿建N*(N-1)/2道路,他建了两个相连的中转站s1,s2,每个牛棚建一条路到其中一个中转站,但是有的牛互相憎恨,就不能建在同一个中转站,而有的牛互相喜欢,必须建在同一中转站。问是否可以建出这样的道路,不可以输出-1,可以输出两两牛棚最小的最大曼哈顿距离。

思路:

将两个中转站看成问题关键,看成矛盾点,一个牛棚不可以连到两个中转站。

2-sat主要是建图:

我们将 i表示连接到s1,i+n(后面用i'表示)表示连接到s2。

而互相憎恨的牛不能连相同中转站例如: 两个牛棚为 i,j  连接方式:i->j'  ,j->i' ,i'->j ,j'->i;

互相喜欢的 : i->j , j->i ,i'->j' ,j'->i';

每种情况都要表示出来。

之后用二分最小的最大曼哈顿距离。怎么判断呢?

将连接方式当成限制条件。两两有四种连接情况(当然我们只指出不能成立的情况,及大于二分的距离,如果成立还要限制条件干嘛):

1.i,j连向s1  dis(i,s1)+dis(j,s1)>d   d为二分距离

  连接方式: i->j'  j->i'  (很显然的意思,i,j都连接s1是不可能的,那当然 当i连接s1是,j必须连接s2了,j也一样)

2.i,j连向s2  dis(i,s2)+dis(j,s2)>d

  连接方式:i'->j  j'->i

3.i连接s1,j连接s2  dis(i,s1)+dis(j,s2)+dis(s1,s2)>d  (一定记得当连接不同时,要加s1到s2的距离)

  连接方式:i->j  j'->i'

4.i连接s2,j连接s1  dis(i,s2)+dis(j,s1)+dis(s1,s2)>d

  连接方式: i'->j' j->i

之后只要正常判断2-sat是否可行即可。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=5000;
const int maxm=2000050;
struct edge{
    int u,v,w,next;
}e[maxm];
struct point{
    int x,y,id;
}p[maxn],s1,s2;
int h[maxn],dfn[maxn],low[maxn];
int st[maxn],vis[maxn],belong[maxn];
int n,m,k,cnt,tot,top,num,dis,dis1[maxn],dis2[maxn];
int ax[maxn],ay[maxn],bx[maxn],by[maxn];

void init()
{
    memset(h,-1,sizeof(h));
    memset(dfn,0,sizeof(dfn));
    memset(vis,0,sizeof(vis));
    memset(belong,0,sizeof(maxn));
    cnt=tot=top=num=0;
}

void add(int u,int v)
{
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].next=h[u];
    h[u]=cnt++;
}

int dist(point a,point b){return abs(a.x-b.x)+abs(a.y-b.y);}
//曼哈顿距离 
void tarjan(int u)
{
    low[u]=dfn[u]=++tot;
    vis[u]=1;
    st[++top]=u;
    for(int i=h[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        int t;
        num++;
        do{
            t=st[top--];
            vis[t]=0;
            belong[t]=num;
        }while(t!=u);
    }
}

bool jug(int x)
{
    init();//记得每次二分都要初始化 
    for(int i=1;i<=n;i++)//连接限制条件 
    {
        for(int j=i+1;j<=n;j++)
        {
            if(dis1[i]+dis1[j]>x)
                add(i,j+n),add(j,i+n);
            if(dis2[i]+dis2[j]>x)
                add(i+n,j),add(j+n,i);
            if(dis1[i]+dis2[j]+dis>x)
                add(i,j),add(j+n,i+n);
            if(dis2[i]+dis1[j]+dis>x)
                add(i+n,j+n),add(j,i);
        }
    }
    for(int i=0;i<m;i++)//憎恨 
    {
        add(ax[i],ay[i]+n),add(ax[i]+n,ay[i]);
        add(ay[i],ax[i]+n),add(ay[i]+n,ax[i]);
    }
    for(int i=0;i<k;i++)//喜欢 
    {
        add(bx[i],by[i]),add(by[i],bx[i]);
        add(bx[i]+n,by[i]+n),add(by[i]+n,bx[i]+n);
    }
    for(int i=1;i<=2*n;i++)
        if(!dfn[i])
            tarjan(i);
    for(int i=1;i<=n;i++)//判断 
        if(belong[i]==belong[i+n])
            return false;
    return true;
}

int main()
{
    std::ios::sync_with_stdio(false);
    while(cin>>n>>m>>k)
    {
        int u,v;
        int maxx=0;
        cin>>s1.x>>s1.y>>s2.x>>s2.y;
        for(int i=1;i<=n;i++)
        {
            cin>>p[i].x>>p[i].y;
            dis1[i]=dist(p[i],s1);//记录到s1的距离 
            dis2[i]=dist(p[i],s2);//记录到s2的距离 
            maxx=max(maxx,max(dis1[i],dis2[i]));
        }
        dis=dist(s1,s2);
        for(int i=0;i<m;i++)//互相憎恨 
            cin>>ax[i]>>ay[i];
        for(int i=0;i<k;i++)//互相喜欢 
            cin>>bx[i]>>by[i];
        int l=0,r=maxx*2+dis,ans=-1;
        while(l<=r)//二分 
        {
            int mid=(l+r)/2;
            if(jug(mid))
            {
                ans=mid;
                r=mid-1;
            }
            else
                l=mid+1;
        }
        cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xiongtao/p/10631516.html
今日推荐