본문 바로가기
알고리즘

백준 아기상어 (Go)

by PudgeKim 2021. 4. 7.

문제 링크: www.acmicpc.net/problem/16236

 

16236번: 아기 상어

N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가

www.acmicpc.net

 

삼성 기출문제로 꽤 까다로운 문제입니다. 문제를 보면 다들 BFS로 풀어야 한단건 느끼셨을 겁니다.

문제 조건을 보면 거리가 같은 경우 위쪽을 먼저 탐색해야하고 위쪽에 여러개 있는 경우 제일 왼쪽을 탐색해야한다고 나와있습니다.

그래서 dx = (-1, 0, 1, 0)  dy = (0, -1, 0, 1) 이런식으로 위쪽, 왼쪽을 먼저 BFS탐색으로 풀면 되지 않을까라는 생각이 들 수 있습니다.

그러나 위 방식을 이용하여 풀면 틀리게 됩니다.

5 x 5 배열이 있고 (2, 2)부터 아기상어가 시작한다고 할 때 거리가 2인 지점들을 위처럼 BFS방식을 사용해서 탐색하면 순서가 아래와 같습니다.

?  ?  1  ?  ?
?  2  x  3  ?
4  x  S  x  6
?  5  x  7  ?
?  ?  8  ?  ?

모두 거리가 2로 같으므로 위쪽, 왼쪽을 먼저 탐색해야하지만 위 그림을 보면 탐색 순서가 맞지 않습니다.

그러므로 문제를 풀기 위해서는 현재 위치에서 BFS탐색을 하여 잡아먹을 수 있는 물고기들을 모두 알아내고 그 물고기들을 거리와 x좌표(위쪽) y좌표(왼쪽)을 heap에 넣어서 그 기준을 토대로 다음 위치를 정해야 합니다. (또는 그냥 리스트에 넣고 나중에 정렬을 해도 됩니다.)

아래는 Go 코드입니다.

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
package main
 
import (
    "bufio"
    "fmt"
    "os"
    "sort"
    "strconv"
)
 
type Item struct {
    dist int
    x      int
    y    int
}
 
 
type Queue []*Item // bfs탐색시 사용할 큐 
 
func (q *Queue) Push(shark *Item) {
    *= append(*q, shark)
}
 
func (q *Queue) Pop() *Item {
    old := *q
    n := len(old)
    popped := old[0]
    old[0= nil
    *= old[1:n]
    return popped
}
 
var n int  // input에서 처음 받는 n
var dx = [4]int{00-11}
var dy = [4]int{-1100}
 
func Bfs(board [][]int, startX, startY, startSize int*Item {
    visited := make([][]int, n) // 방문 중복방지용
    for i:=0; i<n; i++ {
        for j:=0; j<n; j++ {
            visited[i] = append(visited[i], 0)
        }
    }
 
    
    var fish []*Item  // 잡아먹을 수 있는 물고기들
    q := Queue{}
    q.Push(&Item{0, startX, startY})
    visited[startX][startY] = 1
 
    for {
        if len(q) == 0 { break }
        info := q.Pop()
        x, y, dist := info.x, info.y, info.dist
 
        for i:=0; i<4; i++ {
            nx, ny := x+dx[i], y+dy[i]
            if 0<=nx && nx<&& 0<=ny && ny<&& visited[nx][ny] == 0 {
                if board[nx][ny] != 0 && startSize > board[nx][ny] { // 잡아 먹을 수 있는 경우
                    fish = append(fish, &Item{dist+1, nx, ny})
                    visited[nx][ny] = 1
                } else if startSize == board[nx][ny] || board[nx][ny] == 0 {
                    q.Push(&Item{dist+1, nx, ny})
                    visited[nx][ny] = 1
                }
            }
        }
    }
    
    if len(fish) == 0 {  // nil이 반환되면 더이상 잡아먹을 물고기가 없다는 뜻
        return nil
    }
 
    sort.Slice(fish, func(i, j intbool {  // dist -> 위쪽 -> 왼쪽 순서로 정렬
        if fish[i].dist == fish[j].dist {
            if fish[i].x == fish[j].x {
                return fish[i].y < fish[j].y
            } else {
                return fish[i].x < fish[j].x
            }
        }
        return fish[i].dist < fish[j].dist
    })
 
    return fish[0]
}
 
 
func main() {
    fmt.Scanln(&n)
 
    board := make([][]int, n)
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Split(bufio.ScanWords)
 
    var startX, startY int // 처음 아기상어 위치
    for i:=0; i<n; i++ {
        for j:=0; j<n; j++ {
            scanner.Scan()
            num, _ := strconv.Atoi(scanner.Text())
            board[i] = append(board[i], num)
            if num == 9 {
                startX, startY = i, j
            }
        }
    }
 
    answer := 0
    size, eaten := 20
 
    for {
        board[startX][startY] = 0  // 시작지점은 0으로 바꿔줌
        info := Bfs(board, startX, startY, size)
 
        if info == nil {
            fmt.Println(answer)
            break
        }
 
        dist, x, y := info.dist, info.x, info.y
        
        eaten += 1
        if size == eaten {
            size += 1
            eaten = 0
        }
 
        startX, startY = x, y
        answer += dist
    }
 
}
cs

 

파이썬의 collections.deque 같은 내장 모듈이 없어서 큐를 직접 구현한 것도 있고 전체적으로 코드가 좀 깁니다. 채점시에 20ms정도가 나옵니다.

'알고리즘' 카테고리의 다른 글

백준 사회망 서비스(Python/Go)  (0) 2021.04.15
백준 단지번호 붙이기(Go)  (0) 2021.04.11
백준 사다리 조작(Python)  (0) 2021.04.06
백준 차이를 최대로 (Python/Go)  (0) 2021.04.05
백준 게임개발 (Python/Go)  (0) 2021.04.04