放牧代码和思想
专注自然语言处理、机器学习算法
    This thing called love. Know I would've. Thrown it all away. Wouldn't hesitate.

POJ 2749 Building roads 题解《挑战程序设计竞赛》

目录

挑战程序设计竞赛第二版.jpg

POJ 2749 Building roads 

阳关路与独木桥:有N个农场,其中A对相互讨厌,不能碰面;B对相互喜欢,必须碰面。给定两个中转站S1和S2、各个农场的坐标,让每个农场连接到其中一个中转站。求最小化任意两个农场通过中转站的最大距离,若无法实现,输出-1。

4.3成为图论大师之路 

2-SAT

N个农场分别有2N个节点,对第i个农场而言,节点2*i+k表示连接到第k个中转站,则节点2*i+1-k表示连接到另一个中转站,为了表述方便,定义事件i和事件j表示i和j通过中转站k。

给定一个最大距离d,对i和j两个农场,若通过同一个中转站连接它们的距离大于d,则说明i and j = 0,于是按照公式

A AND B = 0     <=>      A->!B     &&  B->!A

得到蕴涵表达式,并连接相应的节点。

若通过不同的中转站连接,距离大于d,则说明i and !j = 0,也连接相应节点。

对于相互讨厌的i和j,有i xor j = 1;对于相互喜欢的i和j,有i xor j = 0,参考POJ 3678 Katu Puzzle 题解《挑战程序设计竞赛》连接节点。

对于以上所有逻辑表达式,遍历两个k,构图,scc即可。

而对于题目所求的最小化最大值,依然是二分解决。

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

using namespace std;
#define MAX_N 500
#define MAX_V 2 * MAX_N

int V;                      // 顶点数
vector<int> G[MAX_V];       // 图的邻接表表示
vector<int> rG[MAX_V];      // 反向图
vector<int> vs;             // 后序遍历顺序的顶点列表
bool used[MAX_V];           // 访问标记
int cmp[MAX_V];             // 所属强连通分量的拓补序

void add_edge(const int &from, const int &to)
{
    G[from].push_back(to);
    rG[to].push_back(from);
}

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

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

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

    return k;
}

//////////////////////////////////////////////////////////////
int N, A, B;
int Sy[2], Sx[2];
int ys[MAX_N], xs[MAX_N];
int dist;

int dists[MAX_N][2];
int hates[MAX_V][2], likes[MAX_V][2];

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

bool check(const int &d)
{
    for (int i = 0; i < V; ++i)
    {
        G[i].clear();
        rG[i].clear();
    }

    for (int i = 0; i < N; ++i)
    {
        for (int j = i + 1; j < N; ++j)
        {
            for (int k = 0; k < 2; ++k)
            {// A AND B = 0    <=>      A->!B     &&  B->!A
                if (dists[i][k] + dists[j][k] > d)// i 和 j连到同一个中转站
                {// i and j = 0
                    add_edge(2 * i + k, 2 * j + 1 - k);// i -> !j
                    add_edge(2 * j + k, 2 * i + 1 - k);// j -> !i
                }
                if (dists[i][k] + dists[j][1 - k] + dist > d)// 不同中转站
                {// i and !j = 0
                    add_edge(2 * i + k, 2 * j + k);         // i -> j
                    add_edge(2 * j + 1 - k, 2 * i + 1 - k); // !j -> !i
                }
            }
        }
    }

    for (int i = 0; i < A; ++i)
    {
        for (int k = 0; k < 2; ++k)
        {
            add_edge(2 * hates[i][0] + k, 2 * hates[i][1] + 1 - k);
            add_edge(2 * hates[i][1] + k, 2 * hates[i][0] + 1 - k);
        }
    }

    for (int i = 0; i < B; ++i)
    {
        for (int k = 0; k < 2; ++k)
        {
            add_edge(2 * likes[i][0] + k, 2 * likes[i][1] + k);
            add_edge(2 * likes[i][1] + 1 - k, 2 * likes[i][0] + 1 - k);
        }
    }

    scc();
    for (int i = 0; i < N; ++i)
    {
        if (cmp[2 * i + 0] == cmp[2 * i + 1])
        {
            return false;
        }
    }
    return true;
}

int solve()
{
    V = 2 * N;
    dist = manhattan_distance(Sx[0], Sy[0], Sx[1], Sy[1]);
    for (int i = 0; i < N; ++i)
    {
        for (int k = 0; k < 2; ++k)
        {
            dists[i][k] = manhattan_distance(xs[i], ys[i], Sx[k], Sy[k]);
        }
    }

    int max_dist = 0;
    for (int i = 0; i < N; ++i)
    {
        for (int j = i + 1; j < N; ++j)
        {
            for (int k = 0; k < 2; ++k)
            {
                max_dist = max(max_dist, dists[i][k] + dists[j][k]);
                max_dist = max(max_dist, dists[i][k] + dists[j][1 - k] + dist);
            }
        }
    }
    ++max_dist;
    int low = -1, high = max_dist;
    while (high - low > 1)
    {
        const int mid = (high + low) >> 1;
        (check(mid) ? high : low) = mid;
    }
    return high == max_dist ? -1 : high;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    scanf("%d%d%d", &N, &A, &B);
    for (int i = 0; i < 2; ++i)
    {
        scanf("%d%d", Sx + i, Sy + i);
    }
    for (int i = 0; i < N; ++i)
    {
        scanf("%d%d", xs + i, ys + i);
    }
    for (int i = 0; i < A; ++i)
    {
        for (int k = 0; k < 2; ++k)
        {
            scanf("%d", hates[i] + k);
            --hates[i][k];
        }
    }
    for (int i = 0; i < B; ++i)
    {
        for (int k = 0; k < 2; ++k)
        {
            scanf("%d", likes[i] + k);
            --likes[i][k];
        }
    }
    printf("%d\n", solve());
#ifndef ONLINE_JUDGE
    fclose(stdin);
#endif
    return 0;
}
16452910 hankcs 2749 Accepted 5220K 1266MS C++ 4511B 2017-01-06 13:58:19

Reference

http://d.hatena.ne.jp/komiyam/20121016/1350387024

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » POJ 2749 Building roads 题解《挑战程序设计竞赛》

评论 欢迎留言

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

我的作品

HanLP自然语言处理包《自然语言处理入门》