hdu 1815 2-SAT经典题

题意:

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值