题意:
n个农场要连在转接站上 共有两个转接站s1,s2
每个农场可以选择连在s1或者s2上
给出m条限制关系:两个牧场必须连在同一个转接站上 或者 两个必须连在不同的转接站上~
然后给出转接站之间的距离 牧场和转接站之间的距离
问 在满足限制条件的前提下 怎样连接可以使得任意两个牧场之间的联通之后的距离的最大值最小~
思路:
最大值最小 —> 阔以想到二分求解
选一或者选二两个选择 + 二元的限制关系 —>阔以想到2-SAT
二分枚举最大值,然后根据枚举的最大值,判断每一条边可不可以加进去 然后再进一步增加二元限制关系
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 10005;
const int maxm = 200005;
struct Edge{
int to, next;
}edge[maxm];
int head[maxn], tot;
void init(){
tot = 0;
memset(head, -1, sizeof(head));
}
void add_Edge_(int u, int v){
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
bool vis[maxn];
int S[maxn], top;
bool dfs(int u){
if(vis[u^1]) return false;
if(vis[u]) return true;
vis[u] = true;
S[top++] = u;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].to;
if(!dfs(v))
return false;
}
return true;
}
bool Twosat(int n){
memset(vis, false, sizeof(vis));
for(int i = 0; i < n; i += 2){
if(vis[i] || vis[i^1]) continue;
top = 0;
if(!dfs(i)){
while(top) vis[S[--top]] = false;
if(!dfs(i^1)) return false;
}
}
return true;
}
const int N = 505;
int n, a, b;
struct Point {
int x, y;
void read() {
scanf("%d%d", &x, &y);
}
} s1, s2, p[N], A[N * 2], B[N * 2];
int dis(Point a, Point b) {
int dx = a.x - b.x;
int dy = a.y - b.y;
return abs(dx) + abs(dy);
}
int g[N][N][4];
bool judge(int d) {
init();
for (int i = 0; i < a; i++) {
int u = A[i].x-1, v = A[i].y-1;
add_Edge_(u*2, v*2+1);
add_Edge_(v*2, u*2+1);
add_Edge_(u*2+1, v*2);
add_Edge_(v*2+1, u*2);
}
for (int i = 0; i < b; i++) {
int u = B[i].x-1, v = B[i].y-1;
add_Edge_(u*2, v*2);
add_Edge_(v*2, u*2);
add_Edge_(u*2+1, v*2+1);
add_Edge_(v*2+1, u*2+1);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (g[i][j][3] > d){
add_Edge_(i*2+1, j*2+1);
add_Edge_(j*2, i*2);
}
if (g[i][j][2] > d){
add_Edge_(i*2, j*2);
add_Edge_(j*2+1, i*2+1);
}
if (g[i][j][1] > d){
add_Edge_(i*2+1, j*2);
add_Edge_(j*2+1, i*2);
}
if (g[i][j][0] > d){
add_Edge_(i*2, j*2+1);
add_Edge_(j*2, i*2+1);
}
}
}
return Twosat(n*2);
}
int main() {
while (~scanf("%d%d%d", &n, &a, &b)) {
s1.read(); s2.read();
for (int i = 0; i < n; i++) {
p[i].read();
for (int j = 0; j < i; j++) {
g[i][j][0] = dis(p[i], s1) + dis(p[j], s1);
g[i][j][1] = dis(p[i], s2) + dis(p[j], s2);
g[i][j][2] = dis(p[i], s1) + dis(p[j], s2) + dis(s1, s2);
g[i][j][3] = dis(p[i], s2) + dis(p[j], s1) + dis(s1, s2);
}
}
for (int i = 0; i < a; i++) A[i].read();
for (int i = 0; i < b; i++) B[i].read();
int l = 0, r = 7777777;
if (!judge(r)) printf("-1\n");
else {
while (l < r) {
int mid = (l + r) / 2;
if (judge(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", r);
}
}
return 0;
}