欢迎光临
我们一直在努力

UVa 10607 Siege

题目描述

平面国可以表示为一个 M×NM \\times NM×N 的矩形网格,每个格子属于一个省。省是四连通(上下左右相邻)的连通块。地图上的每个省用一个 ASCII\\texttt{ASCII}ASCII 字符标记。

首都标记为字符 A,且首都所在的省不包含地图边界上的任何格子。

邪恶国王想要征服首都。为了包围首都,他必须征服所有与首都相邻的省。两个省相邻当且仅当存在两个格子有一条公共边且分属这两个省。

征服规则如下:

  • 边境省(包含地图边界格子的省)可以直接征服。
  • 非边境省只能通过与已被征服的省相邻来征服。

重要限制:不存在一个格子与超过 333 个其他省相邻,这意味着首都的四个方向上不可能出现四个不同的省。

国王希望用最少的征服省份数完成对首都的包围。请求出这个最小值,如果不可能完成,输出 −1-11

输入格式

多组测试数据。每组第一行包含 MMMNNN3<M,N<2003 < M, N < 2003<M,N<200)。接下来 MMM 行每行 NNN 个字符表示地图。输入以 M=0,N=0M = 0, N = 0M=0,N=0 结束。

输出格式

对于每组数据,输出最少需要征服的省份数,若不可能则输出 −1-11

样例

输入:

5 6
BBBBBB
BCCCBB
BCAbBZ
BDDDbZ
33333Z
3 3
BBB
BAB
0 0

输出:

4
1

题目分析

问题本质

这是一个在省份级别图上的 可达性与最短路径 问题。我们需要:

  • 识别所有省份并建立省份之间的邻接关系。
  • 判断是否存在被首都完全包围的省份(即无法从边境到达)。
  • 在可达的前提下,计算包围首都所需的最少省份数。
  • 关键观察

    • 首都 A 不是边境省,但它可能在内部或边界附近。

    • 由于“一个格子最多邻接 333 个不同省份”的限制,首都最多被 333 个不同省份包围(东南西北四个方向中至少有一对属于同一省份)。

    • 如果首都内部包围着其他省份(即存在某个非 A 省的所有格子都被 A 的格子包围),那么这些省份永远无法被征服,因为:

      • 它们不与边境相连(无法直接征服)。
      • 它们被 A 包围,而 A 未被征服(征服规则不允许先征服 A)。
      • 即使先征服了 A 的邻省,也无法进入 A 内部去征服这些被包围的省。

      因此这种情况应输出 −1-11

    可达性判断

    判断某个非 A 省是否被 A 包围的方法:

    • 从所有边境省出发,在省份邻接图上进行广度优先搜索(BFS\\texttt{BFS}BFS)。
    • 关键:不能经过首都 A(遇到 A 时停止扩展)。
    • 如果最终所有非 `A$ 省都被访问到,说明它们都能从边境到达,否则存在被包围的省份。

    包围首都的最少征服数

    定义:

    • SSS = 与首都相邻的省份集合,m=∣S∣m = |S|m=S
    • 如果 SSS 中存在边境省,那么直接征服 SSS 中的所有省即可,答案为 mmm
    • 否则,SSS 中的所有省都不是边境省,它们需要通过其他省份连通到边境。

    此时,我们需要选择一个边境省作为起点,沿着省份邻接图(不能经过首都)走到 SSS 中的某个省,并沿途征服路径上的所有省。设从某个边境省到 SSS 中某个省的最短路径长度为 nnn(这里的长度定义为 路径上省份的数量,包括终点但不包括起点边境省)。那么:

    • 起点边境省:111 个。
    • 路径上的其他省(不含边境省和终点):n−2n – 2n2 个。
    • 终点省:111 个(属于 SSS)。
    • SSS 中的其他 m−1m – 1m1 个省。
      总计:1+(n−2)+1+(m−1)=m+n−11 + (n – 2) + 1 + (m – 1) = m + n – 11+(n2)+1+(m1)=m+n1

    因此,答案为 m+n−1m + n – 1m+n1,其中 n=min⁡t∈Sdist(n = \\min\\limits_{t \\in S} dist(n=tSmindist(任意边境省,t), t),t)distdistdist 是省份图上不经过首都的最短路径长度(按省份数量计数,起点边境省距离为 111)。

    解题步骤

    第一步:省份标号

    使用 BFS\\texttt{BFS}BFSDFS\\texttt{DFS}DFS 对网格进行四连通区域标记,给每个格子赋予所属省份的编号 0,1,…,K−10, 1, \\dots, K-10,1,,K1。记录首都的省份编号 capIdcapIdcapId

    第二步:构建省份邻接图

    遍历每个格子,检查其四个邻居,若邻居属于不同省份,则在两个省份之间建立无向边(使用集合去重)。

    第三步:判断是否存在被包围的省份

  • 找出所有边境省(包含地图边界格子的省份)。
  • 从所有边境省(除了首都)出发进行 BFS\\texttt{BFS}BFS,扩展时不允许进入首都省。
  • 若所有非首都省份都被访问到,则无包围;否则输出 −1-11
  • 第四步:计算最小征服数

  • 找出所有与首都相邻的省份集合 SSS
  • 检查 SSS 中是否有边境省:
    • 若有,答案为 ∣S∣|S|S
  • 否则,从所有边境省出发(不含首都)进行 BFS\\texttt{BFS}BFS,计算到每个省份的最短距离 dist[p]dist[p]dist[p](起点距离为 111)。
  • 找到 n=min⁡p∈Sdist[p]n = \\min_{p \\in S} dist[p]n=minpSdist[p]
  • 答案为 ∣S∣+n−1|S| + n – 1S+n1
  • 复杂度分析

    • 省份标号:O(M×N)O(M \\times N)O(M×N)
    • 建图:O(M×N)O(M \\times N)O(M×N)
    • BFS\\texttt{BFS}BFSO(K+E)O(K + E)O(K+E),其中 KKK 是省份数(K<100K < 100K<100),EEE 是省份间边数(最多 O(K2)O(K^2)O(K2))。
    • 整体复杂度:O(M×N+K2)O(M \\times N + K^2)O(M×N+K2),足够快。

    代码实现

    // Siege
    // UVa ID: 10607
    // Verdict: Accepted
    // Submission Date: 2026-05-27
    // UVa Run Time: 0.000s
    //
    // 版权所有(C)2026,邱秋。metaphysis # yeah dot net

    #include <bits/stdc++.h>
    using namespace std;

    int M, N;
    vector<string> grid;
    int provId[205][205];
    int provCount;
    vector<pair<int,int>> dirs = {{1, 0}, {1, 0}, {0, 1}, {0, 1}};

    // 四连通区域标记,给每个格子分配省份编号
    void labelProvinces() {
    provCount = 0;
    memset(provId, 1, sizeof(provId));
    for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j) {
    if (provId[i][j] != 1) continue;
    queue<pair<int,int>> q;
    q.push({i, j});
    provId[i][j] = provCount;
    while (!q.empty()) {
    auto [x, y] = q.front(); q.pop();
    for (auto [dx, dy] : dirs) {
    int nx = x + dx, ny = y + dy;
    if (nx >= 0 && nx < M && ny >= 0 && ny < N && provId[nx][ny] == 1 && grid[nx][ny] == grid[x][y]) {
    provId[nx][ny] = provCount;
    q.push({nx, ny});
    }
    }
    }
    ++provCount;
    }
    }

    int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    while (cin >> M >> N, M || N) {
    grid.resize(M);
    for (int i = 0; i < M; ++i) cin >> grid[i];

    labelProvinces();

    // 找到首都的省份编号
    int capId = 1;
    for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j)
    if (grid[i][j] == 'A') capId = provId[i][j];

    // 建图:省份邻接表(使用 set 去重)
    vector<set<int>> adj(provCount);
    for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j) {
    for (auto [dx, dy] : dirs) {
    int ni = i + dx, nj = j + dy;
    if (ni >= 0 && ni < M && nj >= 0 && nj < N) {
    int u = provId[i][j], v = provId[ni][nj];
    if (u != v) adj[u].insert(v);
    }
    }
    }

    // 标记边境省
    vector<bool> isBorder(provCount, false);
    for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j)
    if (i == 0 || i == M 1 || j == 0 || j == N 1)
    isBorder[provId[i][j]] = true;

    // BFS 从所有边境省出发(不经过首都),看能否到达所有非 A 省
    vector<bool> reachable(provCount, false);
    queue<int> q;
    for (int i = 0; i < provCount; ++i)
    if (isBorder[i] && i != capId) {
    reachable[i] = true;
    q.push(i);
    }
    while (!q.empty()) {
    int u = q.front(); q.pop();
    for (int v : adj[u]) {
    if (v == capId) continue;
    if (!reachable[v]) {
    reachable[v] = true;
    q.push(v);
    }
    }
    }

    // 检查是否有非 A 省无法到达
    bool ok = true;
    for (int i = 0; i < provCount; ++i)
    if (i != capId && !reachable[i]) {
    ok = false;
    break;
    }
    if (!ok) {
    cout << "-1\\n";
    continue;
    }

    // 找出与首都相邻的省份集合 S
    set<int> neighborSet;
    for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j) {
    if (provId[i][j] != capId) continue;
    for (auto [dx, dy] : dirs) {
    int ni = i + dx, nj = j + dy;
    if (ni >= 0 && ni < M && nj >= 0 && nj < N) {
    int nid = provId[ni][nj];
    if (nid != capId) neighborSet.insert(nid);
    }
    }
    }

    // 如果 S 中有边境省,直接征服 S 即可
    bool hasBorderNeighbor = false;
    for (int x : neighborSet)
    if (isBorder[x]) {
    hasBorderNeighbor = true;
    break;
    }
    if (hasBorderNeighbor) {
    cout << neighborSet.size() << '\\n';
    continue;
    }

    // 否则 BFS 计算从边境省到 S 的最短距离(路径上的省份数,起点距离为 1)
    vector<int> dist(provCount, 1);
    queue<int> bq;
    for (int i = 0; i < provCount; ++i)
    if (isBorder[i] && i != capId) {
    dist[i] = 1;
    bq.push(i);
    }
    while (!bq.empty()) {
    int u = bq.front(); bq.pop();
    for (int v : adj[u]) {
    if (v == capId) continue;
    if (dist[v] == 1) {
    dist[v] = dist[u] + 1;
    bq.push(v);
    }
    }
    }

    int minDist = 1e9;
    for (int x : neighborSet)
    if (dist[x] != 1)
    minDist = min(minDist, dist[x]);

    cout << neighborSet.size() + minDist 1 << '\\n';
    }

    return 0;
    }

    总结

    本题的关键在于理解“包围”的含义以及“被首都包围的省份”无法被征服的判定。通过构建省份级别的图并执行 BFS\\texttt{BFS}BFS,可以高效地判断可达性并计算最小征服数。题目中“一个格子最多与三个省相邻”的条件隐含了首都最多被三个省包围,但我们的算法并不依赖这一条件(仅用于保证数据合理性)。

    该解法的时间复杂度为 O(M×N+K2)O(M \\times N + K^2)O(M×N+K2),空间复杂度 O(M×N+K2)O(M \\times N + K^2)O(M×N+K2),可以在给定限制内高效运行。

    赞(0)
    未经允许不得转载:171主机测评 » UVa 10607 Siege
    分享到: 更多 (0)

    评论 抢沙发

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