WHU 1548 Home 2-SAT

---恢复内容开始---

题意:

  N个人想回家在至少一个时刻、至多两个时刻。并且,他们每个人都能独自回家。

  定义:ai表示第i个人回家的时间, xij = abs(ai - aj) (i != j).

  在每种时间计划中,定义 y = min{xij}。

  问y最大可能是多少?

解法:

  二分时间差的下界,用2-SAT判断是否可行。下确界即为所求。

代码:

这种是直接在跑的时候建边,是效率最快的  

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int MAXN = 2050;
const int INF = (int)1e9+5;

int a[MAXN];
bool mark[MAXN];
int S[MAXN], c;
int n;

inline int myAbs(int x) {
    return x>0 ? x : -x;
}

void init() {
    memset(mark, false, sizeof(mark));
    memset(S, 0, sizeof(S));
    c = 0;
}

bool dfs(int u, int k) {
    if (mark[u^1]) return false; //如果他的对立面和他同时被标记,那么这种情况不应该发生
    if (mark[u]) return true; //如果他已经被标记

    mark[u] = true; //他被标记
    S[c++] = u; //这个点入栈
    for (int i = 0; i < n*2; i++) if (u!=i && (u^1)!=i) {
        if (myAbs(a[u^1]-a[i])<k) {
            if (!dfs(i, k)) return false;
        }
    }
    return true;
}

bool check(int k) {
    init();
    for (int i = 0; i < n*2; i += 2) {
        if (!mark[i] && !mark[i|1]) { //如果这个点没有被标记
            c = 0;
            if (!dfs(i, k)) { //如果x不能赋值为真
                while (c>0) mark[S[--c]] = false;
                if (!dfs(i|1, k)) return false; //如果x也不能赋值为假,那么无解
            }
        }
    }
    return true;
}

void slove() {
    int l = 0, r = INF, m;
    while (l<r) {
        m = (l+r)>>1;
        if (!check(m)) r = m;
        else l = m+1;
    }
    printf("%d
", r-1);
}

int main() {
    #ifdef Phantom01
        freopen("WHU1548.in", "r", stdin);
    #endif // Phantom01

    while (scanf("%d", &n)!=EOF) {
        int k;
        for (int i = 0; i < n; i++) {
            scanf("%d %d", &k, &a[i<<1]);
            if (k&1) a[i<<1|1] = a[i<<1];
            else scanf("%d", &a[i<<1|1]);
        }
        slove();
    }
    return 0;
}
View Code

下面是先建边,再跑2-SAT

数组存邻接表(本地随机了20组数据,跑了12秒, oj跑了3秒)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 2050;
const int INF = (int)1e9+5;

int Edge[MAXN], next[MAXN*MAXN], to[MAXN*MAXN], used;
int a[MAXN];
bool mark[MAXN];
int S[MAXN], c;
int n;

inline int myAbs(int x) {
    return x>0 ? x : -x;
}

inline void addEdge(int u, int v) {
    next[used] = Edge[u];
    to[used] = v;
    Edge[u] = used++;
}

void init() {
    memset(Edge, -1, sizeof(Edge));
    used = 0;
    memset(mark, false, sizeof(mark));
    memset(S, 0, sizeof(S));
    c = 0;
}

bool dfs(int u, int k) {
    if (mark[u^1]) return false; //如果他的对立面和他同时被标记,那么这种情况不应该发生
    if (mark[u]) return true; //如果他已经被标记

    mark[u] = true; //他被标记
    S[c++] = u; //这个点入栈
    for (int p = Edge[u]; p != -1; p = next[p]) if (!dfs(to[p], k))
        return false;
    return true;
}

bool check(int k) {
    init();

    for (int i = 0; i < n*2; i++) {
        for (int j = 0; j < i; j++) {
            if (i==j || i==(j^1)) continue;
            if (myAbs(a[i]-a[j])<k) {
                addEdge(i^1, j);
                addEdge(j^1, i);
            }
        }
    }

    for (int i = 0; i < n*2; i += 2) {
        if (!mark[i] && !mark[i|1]) { //如果这个点没有被标记
            c = 0;
            if (!dfs(i, k)) { //如果x不能赋值为真
                while (c>0) mark[S[--c]] = false;
                if (!dfs(i|1, k)) return false; //如果x也不能赋值为假,那么无解
            }
        }
    }
    return true;
}

void slove() {
    int l = 0, r = INF, m;
    while (l<r) {
        m = (l+r)>>1;
        if (!check(m)) r = m;
        else l = m+1;
    }
    printf("%d
", r-1);
}

int main() {
    #ifdef Phantom01
        freopen("WHU1548.in", "r", stdin);
    #endif // Phantom01

    while (scanf("%d", &n)!=EOF) {
        int k;
        for (int i = 0; i < n; i++) {
            scanf("%d %d", &k, &a[i<<1]);
            if (k&1) a[i<<1|1] = a[i<<1];
            else scanf("%d", &a[i<<1|1]);
        }
        slove();
    }
    return 0;
}
View Code

下面是vector存边的(本地跑了30秒)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 2050;
const int INF = (int)1e9+5;

vector<int> Edge[MAXN];
int a[MAXN];
bool mark[MAXN];
int S[MAXN], c;
int n;

inline int myAbs(int x) {
    return x>0 ? x : -x;
}

void init() {
    for (int i = 0; i < n*2; i++)
        Edge[i].clear();
    memset(mark, false, sizeof(mark));
    memset(S, 0, sizeof(S));
    c = 0;
}

bool dfs(int u, int k) {
    if (mark[u^1]) return false; //如果他的对立面和他同时被标记,那么这种情况不应该发生
    if (mark[u]) return true; //如果他已经被标记

    mark[u] = true; //他被标记
    S[c++] = u; //这个点入栈
    for (int i = 0; i < Edge[u].size(); i++) if (!dfs(Edge[u][i], k))
        return false;
    return true;
}

bool check(int k) {
    init();

    for (int i = 0; i < n*2; i++) {
        for (int j = 0; j < i; j++) {
            if (i==j || i==(j^1)) continue;
            if (myAbs(a[i]-a[j])<k) {
                Edge[i^1].push_back(j);
                Edge[j^1].push_back(i);
            }
        }
    }

    for (int i = 0; i < n*2; i += 2) {
        if (!mark[i] && !mark[i|1]) { //如果这个点没有被标记
            c = 0;
            if (!dfs(i, k)) { //如果x不能赋值为真
                while (c>0) mark[S[--c]] = false;
                if (!dfs(i|1, k)) return false; //如果x也不能赋值为假,那么无解
            }
        }
    }
    return true;
}

void slove() {
    int l = 0, r = INF, m;
    while (l<r) {
        m = (l+r)>>1;
        if (!check(m)) r = m;
        else l = m+1;
    }
    printf("%d
", r-1);
}

int main() {
    #ifdef Phantom01
        freopen("WHU1548.in", "r", stdin);
    #endif // Phantom01

    while (scanf("%d", &n)!=EOF) {
        int k;
        for (int i = 0; i < n; i++) {
            scanf("%d %d", &k, &a[i<<1]);
            if (k&1) a[i<<1|1] = a[i<<1];
            else scanf("%d", &a[i<<1|1]);
        }
        slove();
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Phantom01/p/3671300.html