由于某个养牛场只能连S1或S2,想到2-SAT,又由于是求最小的最大值,可以二分答案
二分构图时,先把相互讨厌和相互喜欢的加边,然后对每组i和j,X表示到S1的距离,Y表示到S2的距离,i表示与S1相连,~i表示与S2相连,dd表示S1与S2直接的距离
如果Xi+Xj>mid,说明i和j不能同时与S1相连,添加边(i,~j),(j,~i)
如果Yi+Yj>mid,说明i和j不能同时与S2相连,添加边(~i,j),(~j,i)
如果Xi+Yj+dd>mid,说明 i&&~j=0,即不能把i与S1相连的同时,把j与S2相连,则添加边(i,j),(~j,~i)
如果Xj+Yi+dd>mid,说明 ~i&&j=0,即不能把i与S2相连的同时,把j与S1相连,则添加边(~i,~j),(j,i)
用2-SAT解问题时,要找确定的矛盾关系,这也是为什么二分构图时是判断>mid而不是判断<=mid
另外这道题一开始我的思维狭窄了,对于给定的mid值怎样构图时,我在想i和j究竟是同时连S1,或者同时连S2,或者一个连S1,一个连S2,这几种距离到底哪个最大,这样子想太麻烦,回顾上面的做法,我们根据mid值,将四种距离一一进行判断,如果出现矛盾,则添边。2-SAT的解不一定是唯一的,这也与上面每对点最多可能4中连法相符合,我们只要根据可以得到的条件构图,在算法判断是否存在可行解的时候会帮我们挑选一种连法的,并不需要我们在一开始就去指定
代码:
#include<iostream> #include<stack> using namespace std; const int MAX=1005; struct node { int v,next; }g[MAX*MAX]; int dfn[MAX],low[MAX],inStack[MAX],adj[MAX],bel[MAX]; int a[MAX][MAX],b[MAX][MAX],dis1[MAX],dis2[MAX],x[MAX],y[MAX]; int e,ee,n,A,B,index,cnt,sx1,sx2,sy1,sy2,dd; stack<int>s; void add(int u,int v) { g[e].v=v; g[e].next=adj[u]; adj[u]=e++; } void tarjan(int u) { int i,v; dfn[u]=low[u]=++index; s.push(u); inStack[u]=1; for(i=adj[u];i!=-1;i=g[i].next) { v=g[i].v; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(inStack[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { cnt++; do { v=s.top(); s.pop(); inStack[v]=0; bel[v]=cnt; }while(u!=v); } } bool ok(int mid) { int i,j; memset(adj,-1,sizeof(adj)); memset(dfn,0,sizeof(dfn)); e=index=cnt=0; for(i=1;i<=n;i++) { for(j=i+1;j<=n;j++) { if(a[i][j]) { add(i,j+n); add(j,i+n); add(i+n,j); add(j+n,i); } } } for(i=1;i<=n;i++) { for(j=i+1;j<=n;j++) { if(b[i][j]) { add(i,j); add(j,i); add(i+n,j+n); add(j+n,i+n); } } } for(i=1;i<=n;i++) { for(j=i+1;j<=n;j++) { if(dis1[i]+dis1[j]>mid) { add(i,j+n); add(j,i+n); } if(dis2[i]+dis2[j]>mid) { add(i+n,j); add(j+n,i); } if(dis1[i]+dis2[j]+dd>mid) { add(i,j); add(j+n,i+n); } if(dis1[j]+dis2[i]+dd>mid) { add(j,i); add(i+n,j+n); } } } for(i=1;i<=2*n;i++) { if(!dfn[i]) tarjan(i); } for(i=1;i<=n;i++) { if(bel[i]==bel[i+n]) return false; } return true; } int main() { int i,j,k; scanf("%d%d%d",&n,&A,&B); scanf("%d%d%d%d",&sx1,&sy1,&sx2,&sy2); dd=abs(sx1-sx2)+abs(sy1-sy2); for(i=1;i<=n;i++) { scanf("%d%d",&x[i],&y[i]); dis1[i]=abs(x[i]-sx1)+abs(y[i]-sy1); dis2[i]=abs(x[i]-sx2)+abs(y[i]-sy2); } for(k=1;k<=A;k++) { scanf("%d%d",&i,&j); a[i][j]=a[j][i]=1; } for(k=1;k<=B;k++) { scanf("%d%d",&i,&j); b[i][j]=b[j][i]=1; } int l=1,r=9999999,mid,ans=-1; while(l<=r) { mid=(l+r)/2; if(ok(mid)) { ans=mid; r=mid-1; } else l=mid+1; } cout<<ans<<endl; return 0; }