Featured image of post [Algorithm] C++ 백준 3419번 : Racing Car Trail

[Algorithm] C++ 백준 3419번 : Racing Car Trail

백준 3419 Racing Car Trail을 격자 그래프의 이분 그래프로 모델링해 Hopcroft–Karp 최대 매칭과 Dulmage–Mendelsohn 도달성으로 각 시작 칸의 승패(A/B)를 판정합니다. O(E√V) 매칭과 O(V+E) 마킹으로 빠르게 처리하며, 구현 디테일과 복잡도까지 정리.

백준 문제 Racing Car Trail (3419)은 장애물이 있는 "격자(Grid)" 위에서 한 칸씩 번갈아 이동하며 자기 궤적을 다시 밟으면 지는 게임입니다. 각 시작 칸에서 최적 플레이 시 누가 이기는지 출력합니다.

문제 요약

  • 입력: N x E 격자(. 빈칸, X 장애물), 여러 테스트케이스, 마지막 0 0.
  • 출력: 각 칸에서 시작할 때 승자 A(Alice) 또는 B(Bob), 장애물은 X.

핵심 아이디어

  • 빈 칸을 패리티 (i+j)로 이분화해 그래프를 구성합니다.
  • Hopcroft–Karp로 최대 매칭을 구한 뒤, DM 스타일의 교대 경로 도달성으로 "일부 최대 매칭에서 자유로울 수 있는 정점"(freeable)을 판정합니다.
  • 시작 칸이 freeable이면 선공(Alice)이 이길 수 없으므로 B, 아니면 A입니다.

C++ 풀이

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// 더 많은 정보는 https://42jerrykim.github.io 에서 확인하세요.
#include <bits/stdc++.h>
using namespace std;

struct HopcroftKarp {
  int nLeft, nRight;
  vector<vector<int>> adj;
  vector<int> pairU, pairV, dist;

  HopcroftKarp(int leftSize, int rightSize)
      : nLeft(leftSize), nRight(rightSize),
        adj(leftSize), pairU(leftSize, -1), pairV(rightSize, -1),
        dist(leftSize, -1) {}

  void addEdge(int u, int v) { adj[u].push_back(v); }

  bool bfs() {
    queue<int> q;
    for (int u = 0; u < nLeft; ++u) {
      if (pairU[u] == -1) {
        dist[u] = 0;
        q.push(u);
      } else {
        dist[u] = -1;
      }
    }
    bool reachableFreeV = false;
    while (!q.empty()) {
      int u = q.front(); q.pop();
      for (int v : adj[u]) {
        int matchedU = pairV[v];
        if (matchedU != -1 && dist[matchedU] == -1) {
          dist[matchedU] = dist[u] + 1;
          q.push(matchedU);
        }
        if (matchedU == -1) reachableFreeV = true;
      }
    }
    return reachableFreeV;
  }

  bool dfs(int u) {
    for (int v : adj[u]) {
      int matchedU = pairV[v];
      if (matchedU == -1 || (dist[matchedU] == dist[u] + 1 && dfs(matchedU))) {
        pairU[u] = v;
        pairV[v] = u;
        return true;
      }
    }
    dist[u] = -1;
    return false;
  }

  int maxMatching() {
    int matching = 0;
    while (bfs()) {
      for (int u = 0; u < nLeft; ++u) {
        if (pairU[u] == -1 && dfs(u)) ++matching;
      }
    }
    return matching;
  }
};

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

  int N, E; // N: rows, E: cols
  while (cin >> N >> E) {
    if (N == 0 && E == 0) break;
    vector<string> grid(N);
    for (int i = 0; i < N; ++i) cin >> grid[i];

    vector<vector<int>> idU(N, vector<int>(E, -1));
    vector<vector<int>> idV(N, vector<int>(E, -1));
    int uCount = 0, vCount = 0;

    for (int i = 0; i < N; ++i) {
      for (int j = 0; j < E; ++j) {
        if (grid[i][j] != '.') continue;
        if (((i + j) & 1) == 0) idU[i][j] = uCount++;
        else idV[i][j] = vCount++;
      }
    }

    HopcroftKarp hk(uCount, vCount);
    vector<vector<int>> adjV(vCount);

    auto inb = [&](int r, int c) {
      return (r >= 0 && r < N && c >= 0 && c < E);
    };
    const int dr[4] = {-1, 1, 0, 0};
    const int dc[4] = {0, 0, -1, 1};

    for (int i = 0; i < N; ++i) {
      for (int j = 0; j < E; ++j) {
        if (grid[i][j] != '.') continue;
        if (((i + j) & 1) == 0) {
          int u = idU[i][j];
          for (int d = 0; d < 4; ++d) {
            int ni = i + dr[d], nj = j + dc[d];
            if (!inb(ni, nj) || grid[ni][nj] != '.') continue;
            int v = idV[ni][nj];
            if (v != -1) {
              hk.addEdge(u, v);
              adjV[v].push_back(u);
            }
          }
        }
      }
    }

    hk.maxMatching();

    // Uplus: U reachable from free U by alternating paths
    vector<char> Uplus(uCount, false), Vseen1(vCount, false);
    {
      queue<int> qu;
      for (int u = 0; u < uCount; ++u) if (hk.pairU[u] == -1) { Uplus[u] = true; qu.push(u); }
      while (!qu.empty()) {
        int u = qu.front(); qu.pop();
        for (int v : hk.adj[u]) {
          if (hk.pairU[u] != v && !Vseen1[v]) { // unmatched U->V
            Vseen1[v] = true;
            int mu = hk.pairV[v];
            if (mu != -1 && !Uplus[mu]) { Uplus[mu] = true; qu.push(mu); }
          }
        }
      }
    }

    // Vminus: V reachable from free V by alternating paths
    vector<char> Vminus(vCount, false), Useen2(uCount, false);
    {
      queue<int> qv;
      for (int v = 0; v < vCount; ++v) if (hk.pairV[v] == -1) { Vminus[v] = true; qv.push(v); }
      while (!qv.empty()) {
        int v = qv.front(); qv.pop();
        for (int u : adjV[v]) {
          if (hk.pairV[v] != u && !Useen2[u]) { // unmatched V->U
            Useen2[u] = true;
            int mv = hk.pairU[u];
            if (mv != -1 && !Vminus[mv]) { Vminus[mv] = true; qv.push(mv); }
          }
        }
      }
    }

    vector<string> out(N, string(E, 'X'));
    for (int i = 0; i < N; ++i) {
      for (int j = 0; j < E; ++j) {
        if (grid[i][j] == 'X') { out[i][j] = 'X'; continue; }
        if (((i + j) & 1) == 0) {
          int u = idU[i][j];
          bool freeable = Uplus[u];
          out[i][j] = freeable ? 'B' : 'A';
        } else {
          int v = idV[i][j];
          bool freeable = Vminus[v];
          out[i][j] = freeable ? 'B' : 'A';
        }
      }
    }

    for (int i = 0; i < N; ++i) cout << out[i] << '\n';
    cout << '\n';
  }
  return 0;
}

복잡도

  • 매칭: O(E · √V)
  • 도달성 마킹: O(V + E)

참고