题目链接:http://poj.org/problem?id=2749
题意:给出n个牛棚。两个特殊点S1,S2的坐标。S1、S2直接相连。牛棚只能连S1或S2,还有一些限制:某些牛棚(friend)只能连在同一个S,某些牛棚(hate)不能连在同一个S。求一种连法使得最长的牛棚间距离最小(距离是曼哈顿距离)
思路:二分枚举最大值mid,然后重新构图,用2-SAT判定可行性。用Xi表示第i个牛棚连到S1,~Xi表示连到S2。检查每一个约束条件,构图:
1、hate关系的i,j Xi->~Xj ~Xi->Xj Xj->~Xi ~Xj->Xi
2、friend关系的i,j Xi->Xj ~Xi->~Xj Xj->Xi ~Xj->~Xi
3、dist(i,S1)+dist(S1,j)>mid Xi->~Xj Xj->~Xi
dist(i,S2)+dist(S2,j)>mid ~Xj->Xi ~Xi->Xj
dist(i,S1)+dist(S1,S2)+dist(S2,j)>mid Xi->Xj ~Xj->~Xi
dist(i,S2)+dist(S2,S1)+dist(S1,j)>mid ~Xi->~Xj Xj->Xi
#include <stdio.h>
#include <string.h>
#include <stack>
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
struct node
{
int v,next;
};
struct Point
{
int x,y;
};
const int INF=1000000000;
node edges[3000000];
int head[1505],dfn[1505],low[1505],visit[1505],color[1505];
int n,A,B,e,index,cnt;
stack<int> S;
Point s1,s2,p[1505],h[1505],f[1505];
void InPut()
{
int i;
scanf("%d%d%d%d",&s1.x,&s1.y,&s2.x,&s2.y);
for(i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y);
for(i=1;i<=A;i++) scanf("%d%d",&h[i].x,&h[i].y);
for(i=1;i<=B;i++) scanf("%d%d",&f[i].x,&f[i].y);
}
void Add(int u,int v)
{
edges[e].v=v;
edges[e].next=head[u];
head[u]=e++;
}
void Tarjan(int u)
{
int i,v;
low[u]=dfn[u]=++index;S.push(u);visit[u]=1;
for(i=head[u];i!=-1;i=edges[i].next)
{
v=edges[i].v;
if(!dfn[v])
{
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(visit[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
cnt++;
do
{
v=S.top();
S.pop();
visit[v]=0;
color[v]=cnt;
}while(u!=v);
}
}
int TWO_ST()
{
int i;
memset(dfn,0,sizeof(dfn));
memset(visit,0,sizeof(visit));
index=cnt=0;
while(!S.empty()) S.pop();
for(i=1;i<=2*n;i++) if(!dfn[i]) Tarjan(i);
for(i=1;i<=n;i++) if(color[i]==color[i+n]) return 0;
return 1;
}
int ABS(int x)
{
return x>0?x:-x;
}
int Dis(Point a,Point b)
{
return ABS(a.x-b.x)+ABS(a.y-b.y);
}
int OK(int mid)
{
memset(head,-1,sizeof(head));
e=0;
int i,j;
for(i=1;i<=n;i++) for(j=i+1;j<=n;j++)
{
if(Dis(p[i],s1)+Dis(p[j],s1)>mid) Add(i,j+n),Add(j,i+n);
if(Dis(p[i],s2)+Dis(p[j],s2)>mid) Add(i+n,j),Add(j+n,i);
if(Dis(p[i],s1)+Dis(s1,s2)+Dis(s2,p[j])>mid) Add(i,j),Add(j+n,i+n);
if(Dis(p[i],s2)+Dis(s1,s2)+Dis(s1,p[j])>mid) Add(i+n,j+n),Add(j,i);
}
for(i=1;i<=A;i++)
{
Add(h[i].x,h[i].y+n);
Add(h[i].x+n,h[i].y);
Add(h[i].y,h[i].x+n);
Add(h[i].y+n,h[i].x);
}
for(i=1;i<=B;i++)
{
Add(f[i].x,f[i].y);
Add(f[i].x+n,f[i].y+n);
Add(f[i].y,f[i].x);
Add(f[i].y+n,f[i].x+n);
}
return TWO_ST();
}
void Deal()
{
int low=0,high=INF,mid;
while(low<=high)
{
mid=(low+high)>>1;
if(OK(mid)) high=mid-1;
else low=mid+1;
}
if(OK(high)) printf("%d\n",high);
else if(OK(low)) printf("%d\n",low);
else puts("-1");
}
int main()
{
while(scanf("%d%d%d",&n,&A,&B)!=EOF)
InPut(),Deal();
return 0;
}