UVa1466/LA4849 String Phone

UVa1466/LA4849 String Phone

题目链接

本题是2010年icpc亚洲区域赛大田赛区G

题意

平面网格上有n(n≤3000)个单元格,各代表一个重要的建筑物。为了保证建筑物的安全,警察署给每个建筑物派了一名警察,并配发了一些有绳电话以供联络。有绳电话是指长度固定的电话,且电话两端的距离必须保持不变。在本题中,坐标(x1,y1)和(x2,y2)之间的距离为|x1-x2|+|y1-y2|。以无向加权图的形式给出哪些警察之间会使用有绳电话,以及每根绳子的长度,如下图所示,这个图保证是连通的。

现在已经确定每名警察所巡逻的建筑物,请判断是否存在一种方案:每个建筑物选定一个顶点安置电话,使得所有有绳电话都能正常使用。

分析

先说一个坑点:题目说图保证是连通的,实际上可能不连通,要对各个连通分量单独处理。

考虑满足距离要求的顶点其实可以分成两类(0:左下/右上、1:左上/右下)并且只能选择一类,每类也只能选则一个,假定有绳电话一端的建筑物选定了0/1类顶点,则另外一端建筑物选定的顶点类别可以通过二染色确定:此有绳电话权值的奇偶性已知(因为长度已知),另外一端建筑物只有选特定类别的顶点才能维持两端点距离的奇偶性与权值要求的相符,这里暂时不需要准确到距离与权值相同,后面做2-SAT来处理这一点即可。

有绳电话一端的建筑物选定了顶点类别后,其所在连通分量的二染色方案如果不存在,那么这种选择不可行;如果二染色方案存在,根据距离需要权值相同的限制建边,用2-SAT解决:枚举有绳电话两端具体选择的点u,v,此时实际距离如果和权值不相同,则连边 u → v ˜ u\rightarrow \~v u→v˜和 v → u ˜ v\rightarrow \~u v→u˜

AC 代码

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

#define N 3010
int dx[][2] = {{0, 1}, {0, 1}}, dy[][2] = {{0, 1}, {1, 0}}, d[N][N], g0[N][N], g[N<<1][N<<1], c0[N], 
    c[N<<1], f[N], x[N], y[N], color[N], s[N<<1], sn[N<<1], low[N<<1], pre[N<<1], clk, cc, p, m, n;

int find(int x) {
    return x == f[x] ? x : f[x] = find(f[x]);
}

bool bipartite(int u) {
    for (int i=0; i<c0[u]; ++i) {
        int v = g0[u][i], b = ((abs(x[u]-x[v]) + abs(y[u]-y[v])) ^ d[u][v] ^ color[u]) & 1;
        if (color[v] < 0) {
            color[v] = b;
            if (!bipartite(v)) return false;
        } else if (color[v] != b) return false;
    }
    return true;
}

void add_clause(int u, int v) {
    g[u][c[u]++] = v^1; g[v][c[v]++] = u^1;
}

bool dfs(int u) {
    low[u] = pre[u] = ++clk; s[p++] = u;
    for (int i=0, v; i<c[u]; ++i) if (!pre[v = g[u][i]]) {
        if (!dfs(v)) return false;
        low[u] = min(low[u], low[v]);
    } else if (!sn[v]) low[u] = min(low[u], pre[v]);
    if (low[u] == pre[u]) {
        ++cc;
        while (true) {
            if (cc == sn[s[--p]^1]) return false;
            sn[s[p]] = cc;
            if (s[p] == u) break;
        }
    }
    return true;
}

bool check(int r, int b) {
    memset(color, -1, sizeof(color)); color[r] = b;
    if (!bipartite(r)) return false;
    memset(c, p = 0, sizeof(c)); memset(pre, clk = 0, sizeof(pre)); memset(sn, cc = 0, sizeof(sn));
    for (r=1; r<=n; ++r) if (color[r] >= 0) for (int i=0; i<c0[r]; ++i) for (int j=0, a=g0[r][i]; j<2; ++j) {
        int xu = x[r] + dx[color[r]][j], yu = y[r] + dy[color[r]][j], u = r<<1 | j;
        for (int k=0; k<2; ++k) {
            int xv = x[a] + dx[color[a]][k], yv = y[a] + dy[color[a]][k], v = a<<1 | k;
            if (abs(xu-xv)+abs(yu-yv) != d[r][a]) add_clause(u, v);
        }
    }
    for (int u=2, m=(n+1)<<1; u<m; ++u) if (!pre[u] && !dfs(u)) return false;
    return true;
}

void solve() {
    cin >> n;
    for (int i=1; i<=n; ++i) cin >> x[i] >> y[i], c0[i] = 0, f[i] = i;
    cin >> m;
    while (m--) {
        int u, v; cin >> u >> v >> d[u][v];
        d[v][u] = d[u][v]; g0[u][c0[u]++] = v; g0[v][c0[v]++] = u; f[find(u)] = find(v);
    }
    bool ok  = true;
    for (int i=1; i<=n; ++i) if (find(i) == i && !check(i, 0) && !check(i, 1)) {
        ok = false; break;
    }
    cout << (ok ? "possible" : "impossible") << endl;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int t; cin >> t;
    while (t--) solve();
    return 0;
}
相关推荐
陈童学哦3 天前
ICPC区域赛成都站【赛后回顾+总结】
c++·算法·icpc
漂流瓶jz1 个月前
UVA-690 流水线调度 题解答案代码 算法竞赛入门经典第二版
c++·算法·深度优先·aoapc·算法竞赛入门经典·uva
惆怅客1232 个月前
UVa1104/LA5131 Chips Challenge
icpc·uva·final 2011·上下界循环费用流·枚举技巧·补集·优化建图
惆怅客1234 个月前
UVa1321/LA2925 Dice contest
dijkstra·icpc·uva·cerc 2003·路径倍增·稀疏表
惆怅客1235 个月前
UVa11604 General Sultan
图论·dfs·建模·有向图·icpc·uva
匪石15 个月前
K-独立钻石(dfs),G-邪恶铭刻(贪心)
深度优先·贪心·icpc·补题
smile是对你的礼貌~@济南大学6 个月前
部分树上问题及图的联通性(图论学习总结部分内容)
python·算法·图论·icpc·ccpc
smile是对你的礼貌~@济南大学6 个月前
网络流初步(图论学习总结部分内容)
python·算法·图论·icpc·ccpc
smile是对你的礼貌~@济南大学6 个月前
最短路(图论学习总结部分内容)
python·算法·图论·icpc·ccpc