题目链接: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; }