POJ 2749 二分 + 2-SAT

该博客讨论了如何运用二分搜索和2-SAT算法解决一类问题:在限定任意两个粮仓之间的最大距离不超过特定值的情况下,判断是否存在合法的连接方案。通过建立布尔变量和约束条件,并利用图的强连通分量来判断可行性。代码实现中,首先定义布尔变量,然后根据节点关系添加边,最后进行图的强连通分量计算。如果所有节点可以分为不同的连通分量,则存在解决方案。
摘要由CSDN通过智能技术生成
题意

传送门 POJ 2749

题解

最小化最大值问题,二分答案。若任意两个粮仓间的最大距离不能超过某个值,根据这个限制条件使用 2 − S A T 2-SAT 2SAT 判断是否存在合法答案。定义布尔变量

x i 为 真 ⇔ 节 点 i 连 接 S 1 x_i 为真 \Leftrightarrow节点i连接S1 xiiS1 对于记恨的两个节点,有 ¬ ( ( x i ∧ x j ) ∨ ( ¬ x i ∧ ¬ x j ) ) \lnot \big((x_i\land x_j)\lor (\lnot x_i\land\lnot x_j)\big) ¬((xixj)(¬xi¬xj));对于友好的两个节点,有 ¬ ( ( x i ∧ ¬ x j ) ∨ ( ¬ x i ∧ x j ) ) \lnot\big((x_i\land \lnot x_j) \lor (\lnot x_i \land x_j)\big) ¬((xi¬xj)(¬xixj));对于两个节点 i , j i,j i,j 间的距离,有 i , j i,j i,j 连接 S 1 , S 2 S1,S2 S1,S2 2 × 2 2\times 2 2×2 种可能,对于节点间距离大于限制值的情况,记录约束条件,例如 i , j i,j i,j 都连接 S 1 S1 S1,且 i , j i,j i,j 间的距离大于限制值,则有 ¬ ( x i ∧ x j ) \lnot (x_i \land x_j) ¬(xixj)

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxn 505
#define maxv 1005
#define maxd 12000005
int N, A, B, V, sx[2], sy[2];
int X[maxn], Y[maxn], ht[maxv * 2], fd[maxv * 2], dis[2][maxn], cmp[maxv];
bool used[maxv];
vector<int> G[maxv], rG[maxv], vs;

void dfs(int v)
{
    used[v] = 1;
    for (int i = 0; i < G[v].size(); i++)
    {
        int u = G[v][i];
        if (!used[u])
            dfs(u);
    }
    vs.push_back(v);
}

void rdfs(int v, int k)
{
    used[v] = 1, cmp[v] = k;
    for (int i = 0; i < rG[v].size(); i++)
    {
        int u = rG[v][i];
        if (!used[u])
            rdfs(u, k);
    }
}

int scc()
{
    vs.clear();
    memset(used, 0, sizeof(used));
    for (int v = 0; v < V; v++)
    {
        if (!used[v])
            dfs(v);
    }
    int k = 1;
    memset(cmp, 0, sizeof(cmp));
    memset(used, 0, sizeof(used));
    for (int i = vs.size() - 1; i >= 0; i--)
    {
        int v = vs[i];
        if (!used[v])
            rdfs(v, k++);
    }
    return k;
}

inline int dist(int x1, int y1, int x2, int y2)
{
    return abs(x1 - x2) + abs(y1 - y2);
}

void add_edge(int u, int v)
{
    G[u].push_back(v);
    rG[v].push_back(u);
}

bool judge(int x)
{
    for (int v = 0; v < V; v++)
    {
        G[v].clear();
        rG[v].clear();
    }
    for (int k = 0; k < A; k++)
    {
        int i = ht[k], j = ht[A + k];
        add_edge(i, N + j);
        add_edge(j, N + i);
        add_edge(N + i, j);
        add_edge(N + j, i);
    }
    for (int k = 0; k < B; k++)
    {
        int i = fd[k], j = fd[B + k];
        add_edge(i, j);
        add_edge(N + j, N + i);
        add_edge(N + i, N + j);
        add_edge(j, i);
    }
    int d12 = dist(sx[0], sy[0], sx[1], sy[1]);
    for (int i = 0; i < N; i++)
    {
        for (int j = i + 1; j < N; j++)
        {
            if (dis[0][i] + dis[0][j] > x)
            {
                add_edge(i, N + j);
                add_edge(j, N + i);
            }
            if (dis[1][i] + dis[1][j] > x)
            {
                add_edge(N + i, j);
                add_edge(N + j, i);
            }
            if (dis[0][i] + dis[1][j] + d12 > x)
            {
                add_edge(i, j);
                add_edge(N + j, N + i);
            }
            if (dis[1][i] + dis[0][j] + d12 > x)
            {
                add_edge(N + i, N + j);
                add_edge(j, i);
            }
        }
    }
    scc();
    for (int i = 0; i < N; i++)
    {
        if (cmp[i] && cmp[N + i] && cmp[i] == cmp[N + i])
            return 0;
    }
    return 1;
}

int main()
{
    scanf("%d%d%d", &N, &A, &B);
    scanf("%d%d%d%d", sx, sy, sx + 1, sy + 1);
    for (int i = 0; i < N; i++)
    {
        scanf("%d%d", X + i, Y + i);
    }
    for (int i = 0; i < A; i++)
    {
        scanf("%d%d", ht + i, ht + A + i);
        --ht[i], --ht[A + i];
    }
    for (int i = 0; i < B; i++)
    {
        scanf("%d%d", fd + i, fd + B + i);
        --fd[i], --fd[B + i];
    }
    for (int i = 0; i <= 1; i++)
    {
        for (int j = 0; j < N; j++)
        {
            dis[i][j] = dist(sx[i], sy[i], X[j], Y[j]);
        }
    }
    V = N << 1;
    int lb = -1, ub = maxd;
    while (ub - lb > 1)
    {
        int mid = (lb + ub) >> 1;
        if (judge(mid))
            ub = mid;
        else
            lb = mid;
    }
    printf("%d\n", ub == maxd ? -1 : ub);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值