정화 코딩

[C++] 벽 부수고 이동하기 본문

PS

[C++] 벽 부수고 이동하기

jungh150c 2024. 11. 26. 02:34

https://www.acmicpc.net/problem/2206

 

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int n, m;
    cin >> n >> m;
    vector<vector<bool>> g(n, vector<bool>(m));
    vector<vector<bool>> chk(n, vector<bool>(m, 0));

    for (int i = 0; i < n; i++) {
        string s;
        cin >> s;
        for (int j = 0; j < m; j++) {
            g[i][j] = (s[j] == '1');
        }
    }

    queue<vector<int>> q;
    // {x index, y index, didBreak, count}
    q.push({0, 0, 0, 1});
    chk[0][0] = true;
    int ans = -1;
    while (!q.empty()) {
        int curi = q.front()[0];
        int curj = q.front()[1];
        int didBreak = q.front()[2];
        int cnt = q.front()[3];
        if (curi == n - 1 && curj == m - 1) {
            ans = cnt;
            break;
        }
        q.pop();
        for (int k = 0; k < 4; k++) {
            int nxti = curi + dx[k];
            int nxtj = curj + dy[k];
            if (nxti >= 0 && nxti < n && nxtj >= 0 && nxtj < m && !chk[nxti][nxtj]) {
                if (!g[nxti][nxtj]) { // 0
                    q.push({nxti, nxtj, didBreak, cnt + 1});
                    chk[nxti][nxtj] = true;
                } else if (g[nxti][nxtj] && didBreak == 0) { // 1
                    q.push({nxti, nxtj, didBreak + 1, cnt + 1});
                    chk[nxti][nxtj] = true;
                }
            }
        }
    }

    cout << ans << '\n';
}

처음에 이렇게 코드를 짰다. 예제는 정상적으로 출력되는데 15% 쯤에서 틀렸습니다가 나왔다. (WA)

 

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int n, m;
    cin >> n >> m;
    vector<vector<bool>> g(n, vector<bool>(m));
    vector<vector<vector<bool>>> chk(n, vector<vector<bool>>(m, vector<bool>(2, 0)));

    for (int i = 0; i < n; i++) {
        string s;
        cin >> s;
        for (int j = 0; j < m; j++) {
            g[i][j] = (s[j] == '1');
        }
    }

    queue<vector<int>> q;
    // {x index, y index, didBreak, count}
    q.push({0, 0, 0, 1});
    chk[0][0][0] = true;
    int ans = -1;
    while (!q.empty()) {
        int curi = q.front()[0];
        int curj = q.front()[1];
        int didBreak = q.front()[2];
        int cnt = q.front()[3];
        if (curi == n - 1 && curj == m - 1) {
            ans = cnt;
            break;
        }
        q.pop();
        for (int k = 0; k < 4; k++) {
            int nxti = curi + dx[k];
            int nxtj = curj + dy[k];
            if (nxti >= 0 && nxti < n && nxtj >= 0 && nxtj < m) {
                if (!g[nxti][nxtj] && !chk[nxti][nxtj][didBreak]) { // 0
                    q.push({nxti, nxtj, didBreak, cnt + 1});
                    chk[nxti][nxtj][didBreak] = true;
                } else if (g[nxti][nxtj] && didBreak == 0 && !chk[nxti][nxtj][didBreak + 1]) { // 1
                    q.push({nxti, nxtj, didBreak + 1, cnt + 1});
                    chk[nxti][nxtj][didBreak + 1] = true;
                }
            }
        }
    }

    cout << ans << '\n';
}

문제점

- 방문 체크(chk) 배열이 단일 구조로 되어 있어, 벽을 부순 경로와 부수지 않은 경로를 구분하지 않음.

- 결과적으로, 벽을 부수지 않고 도달할 수 있는 더 유리한 경로가 무시되거나, 잘못된 최단 경로가 계산될 수 있음.

수정 사항

chk 배열을 3차원으로 확장: chk[n][m][2]로 선언하여 벽을 부수지 않은 경우(didBreak = 0)와 부순 경우(didBreak = 1)를 구분.

- 조건문 수정:
    - 벽이 없는 경우: chk[nxti][nxtj][didBreak]로 방문 여부 체크.
    - 벽이 있는 경우: 벽을 부수지 않았을 때만(didBreak == 0) chk[nxti][nxtj][1]로 방문 처리.

수정해야 하는 이유

- 벽을 부순 경로와 부수지 않은 경로는 다른 경로로 취급해야 하므로 각각 독립적으로 방문 여부를 기록해야 함.

- 벽을 부수지 않고 도달한 경로는 여전히 벽을 부술 기회를 남겨두고 있어 더 유리한 경로 탐색이 가능하므로, 이를 구분하지 않으면 정확한 최단 경로를 계산할 수 없음.

(AC)

 

'PS' 카테고리의 다른 글

[C++] 팰린드롬 (백준 10174번)  (0) 2024.12.13
[C++] A → B (백준 16953번)  (0) 2024.11.27
[C++] 내려가기 (백준 2096번)  (0) 2024.11.25
[C++] 알파벳 (백준 1987번)  (0) 2024.11.24
[C++] 별 찍기 - 11 (백준 2448번)  (0) 2024.11.23
Comments