코딩 공부/백준

[백준][C++] 16137 견우와 직녀

김 정 환 2021. 3. 27. 12:54
반응형

www.acmicpc.net/problem/16137

 

16137번: 견우와 직녀

견우와 직녀는 여러 섬과 절벽으로 이루어진 지역에서 살고 있다. 이 지역은 격자로 나타낼 수 있으며, 상하좌우로 인접한 칸으로 가는 데 1분이 걸린다. 7월 7일은 견우와 직녀가 오작교를 건너

www.acmicpc.net

 

 

 

알고리즘 종류

    - 구현

    - BFS

 

 

 

사고 과정

    - 조건을 다음과 같다.

        * 오작교 조건

            1. 1분 동안 유지

            2. 주기대로 생성

            3. 교차 절벽에 생성 금지

            4. 이미 생성된 절벽에 오작교 겹쳐서 생성 금지

 

        * 다리 건너는 조건

            1. 오작교가 M주기로 생성될 때, 견우는 오작교 주변에서  M-1시간이어야 한다. (본인은 이 조건이 이해가 안되서 고생했다. 마치, 건너기 전에 다리가 생성되었는지 모르고 있다가 건너기 위해서 발을 놓는 순간 다리가 생성되는 느낌...? 으로 상상해봤다.)

            2. 2번 연속 오작교를 건널 수 없다.

            3. 다리가 생성될 때까지 제자리에서 기다릴 수 있다.

 

    - 위 조건들을 생각해보면, 본인은 아래와 같은 생각을 했다.

        * 보통 isVisited로 재방문 금지하고, 첫 방문이면 바로 체크해왔지만, 여기에서는 제자리에서 대기하는 경우가 있기 때문에 isVisited를 바로 체크하지 않는다.

        * Queue에 넣을 변수들은 현재 위치, 시간, 다리 건넌 여부가 될 것이다.

 

 

 

구현(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
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#define MAX 987654321
 
using namespace std;
 
int n, m;
int ans = MAX;
 
int mat[11][11];
int isVisited[11][11];
int dir[4][2= {{1,0}, {0,1}, {-1,0}, {0,-1}};
 
vector<pair<intint> > bridges;
 
 
bool is_valid(int y, int x){
    return (y>=0 && y<n) && (x>=0 && x<n);
}
 
// 교차 절벽인지 확인  
bool is_avail(int y, int x){
    if(is_valid(y+1, x) && is_valid(y, x+1&& mat[y+1][x]==0 && mat[y][x+1]==0return false;
    if(is_valid(y, x+1&& is_valid(y-1, x) && mat[y][x+1]==0 && mat[y-1][x]==0return false;
    if(is_valid(y-1, x) && is_valid(y, x-1&& mat[y-1][x]==0 && mat[y][x-1]==0return false;
    if(is_valid(y, x-1&& is_valid(y+1, x) && mat[y][x-1]==0 && mat[y+1][x]==0return false;
    return true;
}
 
 
void bfs(){
    memset(isVisited, 0sizeof(isVisited));
    queue<pair<pair<intint>pair<intint> > > q; // y, x, time, crossed
    q.push({{0,0}, {0,0}});
    isVisited[0][0= 1;
    
    while(!q.empty()){
        int cy = q.front().first.first;
        int cx = q.front().first.second;
        int ct = q.front().second.first;
        int flag = q.front().second.second;
        q.pop();
        
        for(int d=0; d<4; d++){
            int ny = cy + dir[d][0];
            int nx = cx + dir[d][1];
            
            // 종 료  
            if(ny==n-1 && nx==n-1){
                ans = min(ans, ct+1);
                return;
            }
            
            if(!is_valid(ny, nx)) continue;
            if(isVisited[ny][nx]) continue;
            
            // 절벽이면 무시한다. 
            if(mat[ny][nx]==0continue;
            // 일반 땅이면 그냥 간다. 
            else if(mat[ny][nx]==1){
                isVisited[ny][nx] = 1;
                q.push({{ny, nx}, {ct+10}});
            }
            // 오작교가 만들어질 곳이면 이전에 오작교를 건넜는지 확인한다. 
            else if(mat[ny][nx]>=2 && !flag){
                if((ct+1) % mat[ny][nx] == 0){ // 지금 시간이 오작교 생성시간이라면 건넌다. 
                    isVisited[ny][nx] = 1;
                    q.push({{ny, nx}, {ct+11}});
                }
                else{// 오작교 생성시간이 아니라면 기다려본다. 더 빠르게 갈 수도 있다. 
                    q.push({{cy, cx}, {ct+10}});
                }
            }
        }
        
        
    }
}
 
int main(void){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin >> n >> m;
    
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            cin >> mat[i][j];
        }
    }
    
    // 교차되는 절벽을 제외한 절벽들 찾기  
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            if(mat[i][j] == 0 && is_avail(i, j)) 
                bridges.push_back({i, j}); 
        }
    }
    
    // 절벽 하나 하나에 오작교 놓아보기  
    for(int i=0; i<bridges.size(); i++){
        mat[bridges[i].first][bridges[i].second] = m;
        bfs();
        mat[bridges[i].first][bridges[i].second] = 0;
    }
    
    cout << ans << endl;
}
cs

 

 

 

시행착오

    - 절벽 교차되는 지점을 찾기 위한 방법을 최대한 간단하게 구현하려고 온갖 방법을 생각해 보았다. 시간 많이 잡아먹었다. 역시나 직관적으로 해석이 가능한 구현하는 것이 가장 먼저인 것 같다.

    - 시간 문제를 때때로 곤혹스럽다. 이번에도 그랬다. 오작교 생성이 5분마다 된다고 하면, 주변에 4분에는 와 있어야 한다. 이렇게 한글로 예시를 들어 설명하면 자명한 것 같지만, 본인은 문제 풀이에서 난해함을 느꼈다... (나만 그런가...?) 오작교가 놓일 곳에 발을 놓기 위한 인식 단계에는 오작교가 없어도 되지만, 발을 놓는 순간에 오작교는 있어야 한다. 라고 해석했다. 각설하고, 문제 내에서는 M-1에 주변에 있어야 한다는 것이다.

반응형

'코딩 공부 > 백준' 카테고리의 다른 글

[백준][C++] 14950 정복자  (0) 2021.03.27
[백준][C++] 2307 도로검문  (0) 2021.03.27
[백준][C++] 16954 움직이는 미로 탈출  (0) 2021.03.26
[백준][C++] 16929 Two Dots  (0) 2021.03.26
[백준][C++] 2234 성곽  (0) 2021.03.24