题目概述
有两个中转点和n个谷仓,每个谷仓只能连向两个中转点的一个。某些谷仓中的牛互相厌恶,不能同时连向同一个中转点,某些谷仓中的牛是朋友,必须同时连向同一个中转点。求一种方案使得谷仓之间的曼哈顿距离的最大值最小。
解题报告
每个谷仓只能连接中转点1(S1)和中转点2(S2)的一个,并且还有很多限制条件,我们不难发现这是2-SAT。但无论是暴力还是Tarjan,都不具备求距离最大值最小的功能,而求最大值最小经常是二分的题目,所以我们想到二分枚举答案mid。
厌恶和朋友的限制比较明显,但我们还需要用到答案mid来进行进一步的限制。由于所有谷仓之间的距离都不大于mid,那么对于两个谷仓i,j有下面四种情况:
1.dis(i,S1)+dis(S1,j)>MAX,意味着i谷仓和j谷仓不能同时选S1。
2.dis(i,S2)+dis(S2,j)>MAX,意味着i谷仓和j谷仓不能同时选S2。
3.dis(i,S1)+dis(S1,S2)+dis(S2,j)>MAX,意味着i谷仓选了S1,j谷仓就不能选S2,且j谷仓选了S2,i谷仓就不能选S1。
4.dis(i,S2)+dis(S2,S1)+dis(S1,j)>MAX,意味着i谷仓选了S2,j谷仓就不能选S1,且j谷仓选了S1,i谷仓就不能选S2。
接下来用Tarjan判断2-SAT是否有解即可。
建边的时候需要仔细,不能有遗漏,否则就会出错。
示例程序
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=500,maxA=1000,maxB=1000,maxm=(maxA+maxB)*4+maxn*maxn*8;
int n,A,B,Sx[2],Sy[2],D;
int x[maxn+5],y[maxn+5],Aa[maxA+5],Ab[maxA+5],Ba[maxB+5],Bb[maxB+5];
int E,lnk[2*maxn+5],nxt[maxm+5],son[maxm+5];
int ti,tot,dfn[2*maxn+5],low[2*maxn+5],SCC[2*maxn+5];
int top,stk[2*maxn+5];bool instk[2*maxn+5];
int absi(int x) {if (x<0) return -x; else return x;}
int getdis(int i,int j) {return absi(x[i]-Sx[j])+absi(y[i]-Sy[j]);}
void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[x];lnk[x]=E;}
void Build(int MAX)
{
E=0;memset(lnk,0,sizeof(lnk));
for (int i=1;i<=A;i++)
{
Add(Aa[i]<<1,Ab[i]<<1^1);Add(Ab[i]<<1,Aa[i]<<1^1);
Add(Aa[i]<<1^1,Ab[i]<<1);Add(Ab[i]<<1^1,Aa[i]<<1);
}
for (int i=1;i<=B;i++)
{
Add(Ba[i]<<1,Bb[i]<<1);Add(Bb[i]<<1^1,Ba[i]<<1^1);
Add(Ba[i]<<1^1,Bb[i]<<1^1);Add(Bb[i]<<1,Ba[i]<<1);
}
for (int i=0;i<n-1;i++)
for (int j=i+1;j<n;j++)
{
if (getdis(i,0)+getdis(j,0)>MAX) Add(i<<1,j<<1^1),Add(j<<1,i<<1^1);
if (getdis(i,1)+getdis(j,1)>MAX) Add(i<<1^1,j<<1),Add(j<<1^1,i<<1);
if (getdis(i,0)+D+getdis(j,1)>MAX) Add(i<<1,j<<1),Add(j<<1^1,i<<1^1);
if (getdis(i,1)+D+getdis(j,0)>MAX) Add(i<<1^1,j<<1^1),Add(j<<1,i<<1);
}
}
void Tarjan(int x)
{
dfn[x]=low[x]=++ti;instk[x]=true;stk[++top]=x;
for (int j=lnk[x];j;j=nxt[j])
if (!dfn[son[j]]) Tarjan(son[j]),low[x]=min(low[x],low[son[j]]); else
if (instk[son[j]]) low[x]=min(low[x],dfn[son[j]]);
if (dfn[x]==low[x])
{
int y;tot++;
do y=stk[top--],instk[y]=false,SCC[y]=tot;
while (x!=y);
}
}
bool Two_SAT()
{
memset(dfn,0,sizeof(dfn));
for (int i=0;i<2*n;i++) if (!dfn[i]) Tarjan(i);
for (int i=0;i<2*n;i+=2) if (SCC[i]==SCC[i^1]) return false;
return true;
}
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
scanf("%d%d%d%d%d%d%d",&n,&A,&B,&Sx[0],&Sy[0],&Sx[1],&Sy[1]);
D=absi(Sx[0]-Sx[1])+absi(Sy[0]-Sy[1]);
for (int i=0;i<n;i++) scanf("%d%d",&x[i],&y[i]);
for (int i=1;i<=A;i++) scanf("%d%d",&Aa[i],&Ab[i]),Aa[i]--,Ab[i]--;
for (int i=1;i<=B;i++) scanf("%d%d",&Ba[i],&Bb[i]),Ba[i]--,Bb[i]--;
int L=0,R=12000000;
while (L<=R)
{
int mid=L+(R-L>>1);Build(mid);
if (Two_SAT()) R=mid-1; else L=mid+1;
}
if (L>12000000) L=-1;
return printf("%d\n",L),0;
}