백준 16137 견우와 직녀
본문 바로가기
알고리즘 모음집/알고리즘 (Algorithm)

백준 16137 견우와 직녀

by KyeongMin 2019. 7. 23.
728x90
반응형

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

 

16137번: 견우와 직녀

첫째 줄에 지형의 행과 열의 크기를 나타내는 정수 N (2 ≤ N ≤ 10)과 새로 만들어지는 오작교의 주기를 의미하는 정수 M(2 ≤ M ≤ 20)이 주어진다. 다음 N개의 줄에는 줄마다 배열의 각 행을 나타내는 N개의 정수가 한 개의 빈칸을 사이에 두고 주어진다. 각 칸에 들어가는 값은 0 이상 20 이하이다. 또한, 각 칸에 들어가는 정수의 의미는 다음과 같다. 1: 이동할 수 있는 일반적인 땅 0: 건널 수 없는 절벽 2 이상의 수: 적혀있는 수

www.acmicpc.net

예전에 통과한 문제 였는데 데이터 추가로 인해서 틀렸다로 바꼈습니다. 그래서 다시 통과 상태로 만들기 위해 

문제를 풀었습니다. BFS 탐색으로 문제를 푸는것인데 예전방식도 맞다고 생각 했지만 역시나

알고리즘을 풀때는 애매하면 언젠가 이렇게 틀린문제로 돌아온다는것을 경험하게되었습니다.

 

대략적으로 소스의 방식은 예전에 비해 많이 바뀌었지만 가장 큰 실수를 했던것은 아무래도 오작교를 한개더 

놓을 수 있는 위치를 잘못 한것이 아닐까합니다.

저번에는 전체적인 비엡도 문제가 있었지만 이번에 놓는위치를 확인해주는 소스도 실수가 있었다는것을 알게

되었습니다.

 

저렇게 동그라미가 그려져 있는 부분에는 오작교를 놓을 수없습니다. 이를 걸러주기위해서
인풋으로 들어온곳에 0 인 인덱스의 y,x를 중심으로
4번 검사하면되는데 아래 소스가 위의 그림을 나타냅니다.
이부분을 왜 이렇게 했는지 의문을 가지시는 분이 계실것 같아서
0 일때 +1 이면 1
1일때 2
2일때 3
3일때 0 이되어야하는데 +1를 하면 4가 됩니다. 이때 그것을 0으로 만들어준다고 생각하면
이해하기 편하실것입니다. 
이렇게해서 양쪽이 0이면 놓을 수없는 위치로 false를 반환하고 
그런곳이 없으면 true 참을 반환하여 큐를 돌게 됩니다.
전체적인 큐는 오작교는 두번 연속 건널수 없기때문에
그냥 길인경우와 오작교인 경우를 나누어
오작교인 조건에서는 다음이 오작교라면 이전에 있는위치가 그냥 길인 경우에만 큐에 넣을수 있도록 구현을
하였습니다.
그리고 오작교를 넘어갈때 주기가 맞으면 +1이되서 넘어가지만 그렇지 않은경우 그 남은 주기를 +1과 그 위치의 오작교 주기를 더한것을 빼서 계산을 하며 오작교가 주기가 되었을때 시간을 넣어 최소의 거리로 현재 넘어갈 위치가
이전 보다 더 빨리 도달했으면 최신화를 해주면서 N-1 , N-1에 도달했을때 이동거리의 최소값을 뽑아내는 식으로
문제를 풀었습니다 아래 소스를 참고하시면 될것입니다.
 
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
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
int N, M;
int input[11][11];
int dy[] = { 0,1,0,-1 };
int dx[] = { -1,0,1,0 };
/*
00 01 02
10 11 12
20 21 22
 
*/
int Min = 0x7fffffff;
int visit[11][11];
void init() {
    scanf("%d %d"&N, &M);
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            scanf("%d"&input[i][j]);
        }
    }
}
bool chk(int y, int x) {
    int cnt = 0;
    for (int i = 0; i < 4; i++) {
        int ny = y + dy[i];
        int nx = x + dx[i];
        int ny1 = y + dy[(i + 1) % 4];
        int nx1 = x + dx[(i + 1) % 4];
        if (0 <= ny && 0 <= ny1 && ny < N&&ny1 < N && 0 <= nx && 0 <= nx1 && nx < N&&nx1 < N) {
            if (input[ny][nx] == 0 && input[ny1][nx1] == 0return false;
        }
    }
    
    return true;
}
typedef struct Data {
    int y, x, cnt;
}Data;
void bfs() {
    queue<Data>q;
    q.push({ 000 });
    while (!q.empty()) {
        Data c = q.front(); q.pop();
        if (c.y == N - 1 && c.x == N - 1) {
            if (Min > c.cnt) Min = c.cnt;
        }
        for (int d = 0; d < 4; d++) {
            Data n; n.y = c.y + dy[d]; n.x = c.x + dx[d], n.cnt = c.cnt + 1;
            if (0 <= n.y&&n.y < N && 0 <= n.x&&n.x < N) {
                if (visit[n.y][n.x] > n.cnt&&input[n.y][n.x] == 1) {//그냥 길 이동
                    visit[n.y][n.x] = n.cnt;
                    q.push(n);
                }
                else if (input[n.y][n.x] >= 2) {//오작교인 경우
                    int time = n.cnt%input[n.y][n.x];// 주기가 맞는지 확인
                    if (input[c.y][c.x] == 1 && time == 0 && visit[n.y][n.x] > n.cnt) {// 주기가 맞으면
                        visit[n.y][n.x] = n.cnt;
                        q.push(n);
                    }
                    else if (input[c.y][c.x] == 1 && time != 0) {//주기가 맞지 않으면 계산해서 올라가게 하기
                        n.cnt = n.cnt + input[n.y][n.x] - (n.cnt%input[n.y][n.x]);
                        if (visit[n.y][n.x] > n.cnt) {
                            visit[n.y][n.x] = n.cnt;
 
                            q.push(n);
                        }
                    }
                }
            }
        }
 
    }
 
}
 
int main(void) {
    init();
    memset(visit, 0x32sizeof(visit));
    visit[0][0= 0;
    bfs();
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (input[i][j] == 0 && chk(i, j)) {
                input[i][j] = M;
                memset(visit, 0x32sizeof(visit));
                visit[0][0= 0;
                bfs();
                input[i][j] = 0;
            }
        }
    }
    printf("%d\n", Min);
    return 0;
}
 
 

저번에는 맞다고 생각했던 로직이 틀려서 정말 슬펐습니다. 이런 실수 하지 않도록 더 정확한 알고리즘을

구현하도록 노력해 봅시다. 오늘은 여기까지이고 감사합니다.

 

728x90
반응형

'알고리즘 모음집 > 알고리즘 (Algorithm)' 카테고리의 다른 글

백준 1525 퍼즐  (0) 2019.07.23
백준 10974 모든 순열  (0) 2019.07.23
백준 2309 일곱 난쟁이  (0) 2019.07.21
백준 1062 가르침  (0) 2019.07.21
백준 14391 종이 조각  (0) 2019.07.21

댓글