HDU 1815 Building roads(二分+2-SAT)

来源:互联网 发布:在淘宝刷好评犯法吗 编辑:程序博客网 时间:2024/05/29 18:53

题目地址
题意:给你n个农场,S1场地以及S2场地的坐标,要求每个农场要不与S1连,要不就与S2连,并且每个农场之间的连接距离(曼哈顿距离)的最大值最小,现在告诉你a条不可连接限制,表明两个农场不能和同一个场地连接,又给出b种连接限制,表明两个农场要和同一个场地连接。
思路:我们进行二分枚举最大距离,然后来用2-SAT来判断这个距离的可行性,这里的思想与HDU 3622 Bomb Game的思想十分相像,这里比较复杂的就是进行建图的操作,我们对每个农场进行拆点操作(i*2为与S1相连,i*2+1与S2相连),我们怎么判断一个农场不能与S1或S2中的一个相连,当不能和S1相连,我们只要连一条S1->S2,因为判断的是当同一个地方与S1相连又与S2相连就是错误的,所有既然他不能连S1所以外面连一条S1->S2的边,当有这里有边导致他要连向S1的话那样通过这条边就会使得(i*2和i*2+1出现在同一个连通分量中)后面的不能两个同时连,或者一定要同时连的操作也和这个差不多,只要把一个点的S1连向另一个点的S2就好了,这个操作就是2-SAT的核心(可以看我的博客:传送门)。

#include <iostream>#include <cstring>#include <string>#include <queue>#include <vector>#include <map>#include <set>#include <cmath>#include <cstdio>#include <algorithm>#include <iomanip>#define N 6010#define M 1000010//双倍#define LL __int64#define inf 0x3f3f3f3f#define lson l,mid,ans<<1#define rson mid+1,r,ans<<1|1#define getMid (l+r)>>1#define movel ans<<1#define mover ans<<1|1using namespace std;const LL mod = 1000000007;const double eps = 0.001;int n, a, b;int head[N], idx;struct node {    int to;    int next;}edge[M];struct cows {    int x, y;}place[N], dislike[N], like[N], S1, S2;int dis_S1_S2, S[N], dis_S1[N], dis_S2[N];struct two_SAT {    int dfn[N], low[N];    int stack[N], top;    int belong[N], cnt, num;    bool vis[N];    void init() {        memset(head, -1, sizeof(head));        memset(belong, 0, sizeof(belong));        memset(dfn, 0, sizeof(dfn));        memset(vis, false, sizeof(vis));        num = cnt = 0;        top = 0;        idx = 0;    }    void addedge(int a, int b) {        edge[idx].to = b;        edge[idx].next = head[a];        head[a] = idx++;    }    void tarjin(int u) {//缩点        dfn[u] = low[u] = ++num;        vis[u] = true;        stack[top++] = u;        for (int i = head[u]; ~i; i = edge[i].next) {            int v = edge[i].to;            if (!dfn[v]) {                tarjin(v);                low[u] = min(low[u], low[v]);            }            else if (vis[v]) {                low[u] = min(low[u], dfn[v]);            }        }        if (dfn[u] == low[u]) {            while (true) {                int v = stack[--top];                vis[v] = false;                belong[v] = cnt;//标记联通分量belong                 if (u == v) break;            }            cnt++;        }    }    bool two_sat() {        for (int i = 0; i < 2 * n; i++) {//总点数              if (!dfn[i]) tarjin(i);        }        for (int i = 0; i < 2 * n; i += 2) {//遍历是不是会有一个人有2种可能            if (belong[i] == belong[i + 1]) {                return false;            }        }        return true;    }}sat;bool check(int num) {    sat.init();    bool flag;    for (int i = 0; i < n; i++) {        flag = false;//i*2为与S1相连,i*2+1与S2相连        if (dis_S1[i] > num) {//不能和S1相连            int x = i * 2;//因为判断的是当同一个地方与S1相连又与S2相连就是错误的,所有既然他不能连S1所以外面连一条S1->S2的边,当有这里有边导致他要连向S1的话那样通过这条边就会使得(i*2和i*2+1出现在同一个连通分量中)            int y = i * 2 + 1;            sat.addedge(x, y);            flag = true;        }        if (dis_S2[i] > num) {//不能和S2相连            if (flag) {                return false;            }            int x = i * 2 + 1;            int y = i * 2;            sat.addedge(x, y);        }    }    for (int i = 0; i < n; i++) {        for (int j = i + 1; j < n; j++) {            if (dis_S1[i] <= num&&dis_S1[j] <= num&&dis_S1[i] + dis_S1[j] > num) {//第i个和第j个能够和S1相连的情况                int x = i * 2;                int y = j * 2 + 1;                sat.addedge(x, y);                x = j * 2;                y = i * 2 + 1;                sat.addedge(x, y);            }            if (dis_S2[i] <= num&&dis_S2[j] <= num&&dis_S2[i] + dis_S2[j] > num) {//第i个和第j个能够和S2相连的情况                int x = i * 2 + 1;                int y = j * 2;                sat.addedge(x, y);                x = j * 2 + 1;                y = i * 2;                sat.addedge(x, y);            }            if (dis_S1[i] <= num&&dis_S2[j] <= num&&dis_S1[i] + dis_S2[j] + dis_S1_S2 > num) {//第i个和S1相连,第j个和S2相连                int x = i * 2;                int y = j * 2;                sat.addedge(x, y);                x = j * 2 + 1;                y = i * 2 + 1;                sat.addedge(x, y);            }            if (dis_S2[i] <= num&&dis_S1[j] <= num&&dis_S2[i] + dis_S1[j] + dis_S1_S2 > num) {//第i个和S2相连,第j个和s1相连                int x = i * 2 + 1;                int y = j * 2 + 1;                sat.addedge(x, y);                x = j * 2;                y = i * 2;                sat.addedge(x, y);            }        }    }    for (int i = 0; i < a; i++) {//一定要不同位置        sat.addedge(dislike[i].x * 2, dislike[i].y * 2 + 1);        sat.addedge(dislike[i].x * 2 + 1, dislike[i].y * 2);        sat.addedge(dislike[i].y * 2, dislike[i].x * 2 + 1);        sat.addedge(dislike[i].y * 2 + 1, dislike[i].x * 2);    }    for (int i = 0; i < b; i++) {//一定要相同位置        sat.addedge(like[i].x * 2, like[i].y * 2);        sat.addedge(like[i].x * 2 + 1, like[i].y * 2 + 1);        sat.addedge(like[i].y * 2, like[i].x * 2);        sat.addedge(like[i].y * 2 + 1, like[i].x * 2 + 1);    }    return sat.two_sat();}int main() {    cin.sync_with_stdio(false);    while (cin >> n >> a >> b) {        cin >> S1.x >> S1.y >> S2.x >> S2.y;        dis_S1_S2 = abs(S1.x - S2.x) + abs(S1.y - S2.y);        for (int i = 0; i < n; i++) {            cin >> place[i].x >> place[i].y;            dis_S1[i] = abs(place[i].x - S1.x) + abs(place[i].y - S1.y);            dis_S2[i] = abs(place[i].x - S2.x) + abs(place[i].y - S2.y);        }        for (int i = 0; i < a; i++) {            cin >> dislike[i].x >> dislike[i].y;            dislike[i].x--; dislike[i].y--;        }        for (int i = 0; i < b; i++) {            cin >> like[i].x >> like[i].y;            like[i].x--; like[i].y--;        }        int l = 0, r = 100000000;        int ans = -1;        while (l <= r) {            int mid = (l + r) / 2;            if (check(mid)) {                ans = mid;                r = mid - 1;            }            else                l = mid + 1;        }        cout << ans << endl;    }    return 0;}
原创粉丝点击