2-SAT的最后一道题目,至此2-SAT就算暂时完结了吧。
这个算法真的是一种很神奇的算法,感觉在某些情况下异常好用。
希望比赛碰到题目可以做出来= =
题目大意:
有n个牛舍,现在要在地图上修建两个中转站s1 s2。每个牛舍必须修一条到s1或者s2的道路。其中一些奶牛有仇恨,不能修在同一中转站,一些奶牛是friends,必须修建在同一中转站。问保证在全局最优的情况下,两个牛舍之间最远的距离是多少,注意距离为曼哈顿距离
解题思路:
刚开始完全不知道怎么写,模型是一个很裸的2-SAT模型,但是求取两点间最优情况下的最长路是真心不会。因为2-SAT算法只会输出任意解。但是这个最优情况的最长距离没法确定。
后来看了别人题解发现是直接二分check最长距离。即,如果两个牛舍的距离大于我们当前check的距离,意味着他们两个不能选择他们当前的中转站连接。那么选择相对应的对边即可。最后对于建出的图判断合法性,
Ac代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int INF=1e9+7;
int n,n1,n2;
vector<int> v[maxn],G[maxn];
struct Point
{
int x,y;
}a[maxn],s1,s2;
int tot,cnt,low[maxn],dfn[maxn],belong[maxn];
bool vis[maxn];
stack<int> s;
void tarjan(int u)
{
low[u]=dfn[u]=++tot;
s.push(u),vis[u]=1;
for(int i=0;i<v[u].size();i++)
{
int now=v[u][i];
if(!dfn[now])
{
tarjan(now);
low[u]=min(low[u],low[now]);
}
else if(vis[now])
low[u]=min(low[u],dfn[now]);
}
if(dfn[u]==low[u])
{
cnt++;
while(!s.empty())
{
int now=s.top();
vis[now]=0,s.pop();
belong[now]=cnt;
if(now==u) break;
}
}
}
void add(int x,int y) { G[x].push_back(y); }
int dis(Point p,Point q)
{
return abs(p.x-q.x)+abs(p.y-q.y);
}
bool judge()
{
for(int i=1;i<=n;i++)
if(belong[i]==belong[i+n]) return 0;
return 1;
}
bool check(int distance)
{
tot=0,cnt=0;
for(int i=0;i<=2*n;i++) vis[i]=dfn[i]=low[i]=belong[i]=0,v[i].clear();
for(int i=0;i<=2*n;i++) v[i]=G[i];
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(dis(a[i],s1)+dis(s1,s1)+dis(s1,a[j])>distance) //大于distance表示不能选择当前方案
{
v[i].push_back(j+n);
v[j].push_back(i+n);
}
if(dis(a[i],s1)+dis(s1,s2)+dis(s2,a[j])>distance)
{
v[i].push_back(j);
v[j+n].push_back(i+n);
}
if(dis(a[i],s2)+dis(s2,s1)+dis(s1,a[j])>distance)
{
v[i+n].push_back(j+n);
v[j].push_back(i);
}
if(dis(a[i],s2)+dis(s2,s2)+dis(s2,a[j])>distance)
{
v[i+n].push_back(j);
v[j+n].push_back(i);
}
}
}
for(int i=1;i<=2*n;i++)
if(!dfn[i]) tarjan(i);
if(judge()) return 1; //判断合法性
return 0;
}
int main()
{
scanf("%d%d%d",&n,&n1,&n2);
scanf("%d%d%d%d",&s1.x,&s1.y,&s2.x,&s2.y);
for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
for(int i=1;i<=n1;i++) //根据约束条件建图
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y+n),add(y,x+n);
add(x+n,y),add(y+n,x);
}
for(int i=1;i<=n2;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(x+n,y+n);
add(y,x),add(y+n,x+n);
}
int L=0,R=10000000,res=-1; //二分最长路
while(L<=R)
{
int mid=(L+R)>>1;
if(check(mid)) res=mid,R=mid-1;
else L=mid+1;
}
printf("%d\n",res);
//system("pause");
}