[Swift] 2021 KAKAO BLIND RECRUITMENT ์นด๋ ์ง ๋ง์ถ๊ธฐ

Problem
์ฝ๋ฉํ ์คํธ ์ฐ์ต - ์นด๋ ์ง ๋ง์ถ๊ธฐ
[[1,0,0,3],[2,0,0,0],[0,0,0,2],[3,0,1,0]] 1 0 14 [[3,0,0,2],[0,0,1,0],[0,1,0,0],[2,0,0,3]] 0 1 16
programmers.co.kr
Solution
๋ชจ๋ ์นด๋๋ฅผ ๋ค์ง๋ ์ต์ ์กฐ์ํ์๋ฅผ ์ฐพ์์ผ ํ๋ ๋ฌธ์ ์ ๋๋ค.
(BFS๋ฅผ ๊ตฌํํ๋ ๋ฒ๊ณผ ์์ด๊ตฌํ๋ ๋ฒ์ ์์์ผ ๊ตฌํํ๊ธฐ ์์ํ์ค๊ฒ๋๋ค.)
1. ์นด๋๋ฅผ ๋ค์ง๋ ์์๋ฅผ ์ ํ๊ธฐ(์์ด ๊ตฌํ๋ ๋ฐฉ๋ฒ ์๊ธฐ)
๋ง์ฝ 1,2,3 ์นด๋๊ฐ ์๋ค๋ฉด 1 - 2 - 3, 1 - 3 - 2, 2 - 1 - 3... ๋ฑ์ผ๋ก ๋ค์ง์ ์ ์์ต๋๋ค.
์์ด์ ๊ตฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ๊ท ํจ์๋ฅผ ์ฌ์ฉํด ํด๋น ํ๋์ฉ ์ซ์๋ฅผ ์ง์๋๊ฐ๋ฉด์ depth์ ์ ์ฅํด์ค๋๋ค.
ํ์ง๋ง ์นด๋๋ ๊ฐ ๊ฐ 2์ฅ์ฉ ์์ผ๋ฏ๋ก ๋๊ฐ์ ์ซ์ ์นด๋๋ผ๋ ๋จผ์ ๋ค์ง์ ์นด๋ ์์๋ ์ ํด์ค์ผ ํฉ๋๋ค.
(startCardGame์ ์ฐธ์กฐํด์ฃผ์ธ์!)
2. BFS๋ก ๋ชจ๋ ์ด๋์ ๋ํ ์กฐ์ ํ์ ๊ตฌํ๊ธฐ
์ผ์ชฝ,์ค๋ฅธ์ชฝ,์๋์ชฝ,์์ชฝ์ผ๋ก ์ด๋ํ๋ ๊ฒ๊ณผ ctrl์ ๋๋ฅด๊ณ ์ผ์ชฝ,์ค๋ฅธ์ชฝ,์๋์ชฝ,์์ชฝ๋ก ์ด๋ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ผ๋ฏ๋ก ์ด 8๊ฐ์ง๋ก
์ด๋ํ์ ๋ ์กฐ์ ํ์๋ฅผ ๊ตฌํด์ผ ํฉ๋๋ค.
์ด ๋ BFS๋ก ์ด๋์ ๋ํ ๊ฐ์ queue์ ๋ฃ์ด๋๊ณ queue์ ๊ฐ์ฅ ์ฒซ๋ฒ์งธ ๊ฐ์ ์ญ์ ํด ๋๊ฐ๋ฉด์ ๊ตฌํ ์ ์์ต๋๋ค.
๋ํ 4x4 Bool์ ๋ด์ ๋ฐฐ์ด์ ๋ง๋ค์ด์ ํด๋น ์์น๋ฅผ ๋ฐฉ๋ฌธํ๋์ง ์ํ๋์ง ํ์ธํด์ค๋๋ค.
(moveCurrentToGoal๊ณผ moveBFS๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์!)
3. ๋ชจ๋ ๊ฒฝ์ฐ์ ์์์ ๊ฐ์ฅ ์์ ๊ฐ ์ฐพ๊ธฐ
์ ๋ต(answer) ๋ณ์๋ฅผ ์ ์์์ ๊ฐ์ฅ ํฐ ๊ฐ์ธ Int.max๋ก ๋์ต๋๋ค.
์นด๋๋ฅผ ๋ค์ง์ ๋๋ง๋ค depth์ ์ซ์๋ค์ ๋ด์๋๊ณ ๋ชจ๋ ์ซ์๋ค์ด ๋ค์ง์ด์ก์๋ ์ฆ, depth์ ์นด๋๊ฐฏ์/2๋งํผ์ ์ซ์๊ฐ ์ฑ์์ก์๋
answer์ count๋ฅผ ๋น๊ตํด ๋ ์๋ค๋ฉด answer๋ฅผ count๋ก ๋ฐ๊ฟ์ค๋๋ค.
์ด๋ ๊ฒ ๋ชจ๋ ์์ด์ ๋ํ ์กฐ์ํ์๋ฅผ ๊ตฌํ๊ณ ๋ ์์ ๊ฐ์ผ๋ก answer๋ฅผ ๋ฐ๊ฟ์ค๋๋ค.
๋ง์ง๋ง์ผ๋ก answer์ ์ด์ฐจํผ ์นด๋๋ค์ ๋ค์ง์ด์ง ๋๋ง๋ค +1์ด๋ฏ๋ก ์นด๋์ ๊ฐฏ์๋งํผ์ ๋ํด์ฃผ๊ณ return ํด์ค๋๋ค.
(solution์ ์ฐธ๊ณ ํด์ฃผ์ธ์!)
Source Code
func solution(_ board:[[Int]], _ r:Int, _ c:Int) -> Int { | |
//์ ๋ต | |
var answer = Int.max | |
//์นด๋ ๊ฐฏ์ | |
var cardsCount:Int = 0 | |
//board ์ ์ฒด๋ฅผ ํ์ํด 0์ด ์๋ ์ซ์๋ฅผ ์ธ์ค๋ค.(์นด๋์ ๊ฐฏ์) | |
cardsCount = board.flatMap{$0}.filter{$0 != 0}.count | |
//์นด๋์ ์์น๊ฐ ์ด๋จ๋์ง ์ฐพ์. | |
let cards = findAllNumbersLocation(board: board) | |
//์นด๋๊ฒ์ ์์ | |
startCardGame(answer:&answer,cardsCount:&cardsCount,board: board, start: (c,r), count: 0, cards: cards,depth:[]) | |
//์ด์ฐจํผ ์นด๋ ๊ฐฏ์๋งํผ enter๋ฅผ ํ๋ฏ๋ก ์นด๋ ๊ฐฏ์๋งํผ ๋ํด์ค๋ค. | |
return answer + cardsCount | |
} | |
func startCardGame(answer:inout Int,cardsCount:inout Int,board:[[Int]],start:(Int,Int),count:Int,cards:[[(Int,Int)]],depth:[Int]) { | |
//count๊ฐ answer๋ณด๋ค ํฌ๋ฉด ๋ ์ด์ ์งํํ ํ์ ์์ผ๋ฏ๋ก return | |
if answer != Int.max && count >= answer { | |
return | |
} | |
//depth๊ฐ ์นด๋์ ๊ฐฏ์/2๋ ์ฆ ๋ชจ๋ ์นด๋๋ฅผ ๋ค์ง์ ๊ฒ์ด๊ณ && count๊ฐ answer๋ณด๋ค ์์๋ | |
if depth.count == cardsCount/2 && answer > count { | |
//answer๋ฅผ count๋ก ๋ง๋ค์ด์ค๋ค. | |
answer = count | |
return | |
} | |
//์นด๋๋ฅผ ํ์ | |
for (i,card) in cards.enumerated(){ | |
//์๋ก์ด ๋ณด๋ | |
var newBoard = board | |
//์๋ก์ด ๋์ค | |
var newDepth = depth | |
//์๋ก์ด ์นด๋ | |
var newCards = cards | |
//์นด๋๊ฐ ๋น์๋ค๋ฉด ์งํํ์ง ์์ | |
if card.isEmpty { continue } | |
//์์๋ถํฐ ์ฒซ๋ฒ์งธ ์นด๋๊น์ง ์ฒซ๋ฒ์ฌ ์นด๋๋ถํฐ ๋๋ฒ์งธ ์นด๋๊น์ง์ count | |
let count1 = moveCurrentToGoal(current: start, goal: card[0], count: 0, board: board) + moveCurrentToGoal(current: card[0], goal: card[1], count: 0, board: board) | |
//์์๋ถํฐ ๋๋ฒ์งธ ์นด๋๊น์ง ๋๋ฒ์งธ ์นด๋๋ถํฐ ์ฒซ๋ฒ์งธ ์นด๋๊น์ง์ count | |
let count2 = moveCurrentToGoal(current: start, goal: card[1], count: 0, board: board) + moveCurrentToGoal(current: card[1], goal: card[0], count: 0, board: board) | |
//ํด๋น ๋ฒํธ์ ์นด๋๋ฅผ ๋ชจ๋ ์์ ์ค๋ค. | |
newCards[i] = [] | |
//๋ณด๋์์ ์ฒซ๋ฒ์งธ ์นด๋ ์์น๋ฅผ ์ซ์๋ฅผ 0์ผ๋ก ๋ง๋ค์ด์ค๋ค. | |
newBoard[card[0].1][card[0].0] = 0 | |
//๋ณด๋์์ ๋๋ฒ์งธ ์นด๋ ์์น๋ฅผ ์ซ์๋ฅผ 0์ผ๋ก ๋ง๋ค์ด์ค๋ค. | |
newBoard[card[1].1][card[1].0] = 0 | |
//๋์ค์ ๋ฒํธ๋ฅผ ์ถ๊ฐํด์ค๋ค. | |
newDepth.append(i) | |
//count1๊ณผ count2 ์ค ๋ ์์ ๊ฒ์ count์ ๋ํด์ฃผ๊ณ startCardGame์ ์ฌ๊ทํด์ค. | |
if count1 < count2 { | |
startCardGame(answer:&answer,cardsCount:&cardsCount,board: newBoard, start: card[1], count: count + count1, cards: newCards,depth: newDepth) | |
}else { | |
startCardGame(answer:&answer,cardsCount:&cardsCount,board: newBoard, start: card[0], count: count + count2, cards: newCards,depth: newDepth) | |
} | |
} | |
} | |
//๋ชจ๋ ์ซ์๋ค์ ์์น๋ฅผ ์ฐพ๋ ํจ์ | |
func findAllNumbersLocation(board:[[Int]]) -> [[(Int,Int)]]{ | |
var newCards = Array(repeating: [(Int,Int)](), count: 7) | |
for (y,row) in board.enumerated() { | |
for (x,number) in row.enumerated() { | |
if number != 0 { | |
newCards[number].append((x,y)) | |
} | |
} | |
} | |
return newCards | |
} | |
//ํ์ฌ๋ถํฐ ๋ชฉ์ ์ง๊น์ง ๊ฐ๋ ํจ์ | |
func moveCurrentToGoal(current:(Int,Int),goal:(Int,Int),count:Int,board:[[Int]]) -> Int{ | |
//๊ฐ์ฅ ์์ count๋ฅผ ๋ด์ ๋ณ์ | |
var minCount = Int.max | |
//BFS๊ตฌํ์ ์ํ ๋ณ์ | |
var queue:[((Int,Int),(Int,Int),Int,[[Int]])] = [(current,goal,count,board)] | |
//๋ฐฉ๋ฌธ์ด๋ ฅ์ด ์๋์ง ํ์ธํ ๋ณ์ | |
var visited:[[Bool]] = Array(repeating: Array(repeating:false, count: 4), count: 4) | |
//ํ๊ฐ ๋น์ง ์์๋ค๋ฉด | |
while !queue.isEmpty { | |
//minCount๊ฐ Int.max๊ฐ ์๋๋ ๊ฒ์ ์ด๋ค ์ซ์๊ฐ ๋ค์ด์๋ค๋ ์๋ฏธ์ด๋ฏ๋ก | |
if minCount != Int.max { | |
//minCount๋ฅผ return ํด์ค๋ค. | |
return minCount | |
} | |
//queue์ ๊ฐ์ฅ ์ฒซ๋ฒ์งธ๋ฅผ ์ญ์ ํด์ฃผ๋ฉด์ ์ ์ฅํจ. | |
let first = queue.removeFirst() | |
//๊ฐ์ฅ ํ์ ๊ฐ์ฅ ์ฒซ๋ฒ์งธ ์๋ ๊ฐ์ BFS์ ๋ฃ์ด์ค. | |
moveBFS(current: first.0, goal: first.1, count: first.2,board: first.3,minCount:&minCount,queue:&queue,visited: &visited) | |
} | |
return minCount | |
} | |
//BFS๋ก ํ์ฌ๋ถํฐ ๋ชฉ์ ์ง๊น์ง ์ด๋ํ๋ ํจ์ | |
func moveBFS(current:(Int,Int),goal:(Int,Int),count:Int,board:[[Int]],minCount:inout Int,queue: inout [((Int,Int),(Int,Int),Int,[[Int]])],visited:inout [[Bool]]) { | |
//๋ฐฉ๋ฌธํ๊ฑฐ๋ ํ์ฌ ์์น๊ฐ ๋ฒฝ๋ณด๋ค ๋ ๋ฐ๊นฅ์ด๋ฉด return | |
if current.0 > 3 || current.1 > 3 || current.0 < 0 || current.1 < 0 { | |
return | |
} | |
//๋ฐฉ๋ฌธํ ์ ์ด ์๋ค๋ฉด ํด๋น ์์น๋ฅผ true๋ก ๋ฐ๊ฟ์ค๋ค. | |
if visited[current.1][current.0] == false { | |
visited[current.1][current.0] = true | |
}else { | |
//๋ฐฉ๋ฌธํ๋ค๋ฉด return | |
return | |
} | |
//ํ์ฌ์์น๊ฐ ๋ชฉ์ ์ง์ ๊ฐ๋ค๋ฉด minCount๋ฅผ count๋ก ๋ฐ๊ฟ์ค๋ค. | |
if current == goal{ | |
minCount = count | |
return | |
} | |
//์ผ์ชฝ,์ค๋ฅธ์ชฝ,์์ชฝ,์๋์ชฝ ctrl + ์ผ์ชฝ,์ค๋ฅธ์ชฝ,์์ชฝ,์๋์ชฝ 8๊ฐ์ง ์ด๋์ ๋ด์ ๋ณ์ | |
let moves = [moveLeft(current: current),moveRight(current: current),moveUp(current: current),moveDown(current: current),moveCtrlLeft(goalX: goal.0, currentX: current.0, y: current.1, board: board),moveCtrlRight(goalX: goal.0, currentX: current.0, y: current.1, board: board),moveCtrlUp(goalY: goal.1, currentY: current.1,x: current.0,board: board),moveCtrlDown(goalY: goal.1, currentY: current.1,x: current.0,board: board)] | |
//8๊ฐ์ง ์ด๋์ ๋ชจ๋ ํด์ค๋ค. | |
for move in moves { | |
//ํ์ฌ ์์น๊ฐ ๋ฒฝ ์์ ์๋ค๋ฉด | |
if current.0 <= 3 && current.1 <= 3 && current.0 >= 0 && current.1 >= 0 { | |
//ํ์ ๋ฃ์ด์ค๋ค. | |
queue.append((move,goal,count+1,board)) | |
} | |
} | |
} | |
//์ผ์ชฝ์ผ๋ก 1์นธ ์ด๋ | |
func moveLeft(current:(Int,Int)) -> (Int,Int) { | |
return (current.0-1,current.1) | |
} | |
//์ค๋ฅธ์ชฝ์ผ๋ก 1์นธ ์ด๋ | |
func moveRight(current:(Int,Int)) -> (Int,Int){ | |
return (current.0+1,current.1) | |
} | |
//์๋์ชฝ์ผ๋ก 1์นธ ์ด๋ | |
func moveDown(current:(Int,Int)) -> (Int,Int) { | |
return (current.0,current.1+1) | |
} | |
//์์ชฝ์ผ๋ก 1์นธ ์ด๋ | |
func moveUp(current:(Int,Int)) -> (Int,Int) { | |
return (current.0,current.1-1) | |
} | |
//ctrl + ์ผ์ชฝ์ผ๋ก ์ด๋ | |
func moveCtrlLeft(goalX:Int,currentX:Int,y:Int,board:[[Int]]) -> (Int,Int) { | |
//์ผ์ชฝ์ผ๋ก ์ด๋ํด์ผํ๋๋ฐ ๋ชฉ์ ์ง์ x๊ฐ ํ์ฌ ์์น์ x๋ณด๋ค ๋ ์๊ฑฐ๋ ๊ฐ์ผ๋ฉด ์๋๋ฏ๋ก ๋ฒฝ ๋ฐ๊นฅ์ผ๋ก ๋ฐ์ด๋ฒ๋ฆผ. | |
if goalX >= currentX { | |
return (4,4) | |
} | |
var newX = 0 | |
//๋ชฉ์ ์ง์ ํ์ฌ x์ ์์น ์ฌ์ด์ | |
for x in goalX..<currentX { | |
//์ซ์๊ฐ ์๋ค๋ฉด | |
if board[y][x] != 0 { | |
//newX๋ฅผ ํด๋น ์ซ์๋ก ๋ฐ๊ฟ์ค | |
newX = x | |
} | |
} | |
//์ซ์๊ฐ ์๋ค๋ฉด 0์ผ๋ก ๊ฐ์ฅ ์ผ์ชฝ์ผ๋ก ์ฎ๊น | |
return (newX,y) | |
} | |
//ctrl + ์ค๋ฅธ์ชฝ์ผ๋ก ์ด๋ | |
func moveCtrlRight(goalX:Int,currentX:Int,y:Int,board:[[Int]]) -> (Int,Int){ | |
if goalX <= currentX { | |
return (4,4) | |
} | |
for x in currentX+1...goalX { | |
if board[y][x] != 0 { | |
return (x,y) | |
} | |
} | |
return (3,y) | |
} | |
//ctrl + ์์ชฝ์ผ๋ก ์ด๋ | |
func moveCtrlUp(goalY:Int,currentY:Int,x:Int,board:[[Int]]) -> (Int,Int){ | |
if goalY >= currentY { | |
return (4,4) | |
} | |
var newY = 0 | |
for y in goalY..<currentY { | |
if board[y][x] != 0 { | |
newY = y | |
} | |
} | |
return (x,newY) | |
} | |
//ctrl + ์๋์ชฝ์ผ๋ก ์ด๋ | |
func moveCtrlDown(goalY:Int,currentY:Int,x:Int,board:[[Int]]) -> (Int,Int){ | |
if goalY <= currentY { | |
return (4,4) | |
} | |
for y in currentY+1...goalY { | |
if board[y][x] != 0 { | |
return (x,y) | |
} | |
} | |
return (x,3) | |
} |
P.S
์ฒ์์ ๋ชจ๋ ์นด๋๋ฅผ ๋ค์ง๋ ๊ฒฝ์ฐ์ ์๋ ๊ทธ๋ฅ ๋ชจ๋ ์นด๋๋ฅผ ํ์ํ๊ณ ๋ค์ง์ ์นด๋๋ฅผ ์ง์ฐ๊ณ ๋ ๋จ์ ์นด๋๋ค์
๋๊ฐ์ด ๋ฐ๋ณตํด์ฃผ๋ฉด ๋์ง ์๋? ๋ผ๊ณ ์๊ฐํ๋๋ฐ ์๋๋ค..
๊ทผ๋ฐ ์ค์ ๋ก ์์ด๋ ์ซ์๋ค์ ๋ณด๋ฉด ์์ด๊ณผ ๋๊ฐ์ด ์์ด๊ธดํ๋๋ฐ.. ์ ์๋ ๊น?
BFS๋ก ๊ตฌํํ๊ธฐ ์ ์๋ ํ์ฌ x,y์ ๋ชฉ์ ์ง์ x,y๋ฅผ ์ฐจ์ด๋ฅผ ๋น๊ตํด์ x๋จผ์ ๋ง์ถ๊ณ y๋ฅผ ๋ง์ถ๋์ y ๋จผ์ ๋ง์ถ๊ณ x๋ฅผ ๋ง์ถ๋๋ฅผ
๊ตฌํ๋ฉด ๋๊ฒ ์งํ๋๋ฐ ์๋์ ๊ฐ์ด 4์์ 4๋ก ์ด๋ํ๋ ๊ฒฝ์ฐ๋ y๋ฅผ ํ์นธ๋ง ์์ง์ด๊ณ x์์ ctrl๋ก ์์ง์ด๊ณ
๋ค์ y๋ฅผ ์์ง์ฌ์ผ ๋๋ฏ๋ก ๋ชจ๋ ์ด๋์ ๋ํ ์กฐ์ ํ์๋ฅผ ๊ตฌํด์ค์ผ ํ๋ค.

์ด๊ฑฐ ํ ๋ฌธ์ ํธ๋๋ฐ ๊ฑฐ์ ์ผ์ฃผ์ผ๊ฑธ๋ฆฐ๊ฑฐ ๊ฐ๋ค....
๊ณ ์ง๋ ๋ง์ด ๋ถ๋ฆฌ๊ณ ๋๋ง์ ๋ฐฉ์์ผ๋ก ํ๊ฑฐ์ผ๋ผ๊ณ ์๊ฐํด์ ์ค๋ ๊ฑธ๋ฆฐ๊ฑฐ ๊ฐ๋ค...
๋ค์๋ถํฐ๋ ๋ด๋ ค๋๊ณ ๋ฐฐ์ฐ๋ ๋ง์์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํด์ผ๊ฒ ๋ค.
Reference
2021 ์นด์นด์ค ์ ์ ๊ณต์ฑ 1์ฐจ ์จ๋ผ์ธ ์ฝ๋ฉ ํ ์คํธ for Tech developers ๋ฌธ์ ํด์ค
์ง๋ 2020๋ 9์ 12์ผ ํ ์์ผ ์คํ 2์๋ถํฐ 7์๊น์ง 5์๊ฐ ๋์ 2021 ์นด์นด์ค ์ ์ ๊ฐ๋ฐ์ ๊ณต์ฑ 1์ฐจ ์ฝ๋ฉ ํ ์คํธ๊ฐ ์งํ๋์์ต๋๋ค. ํ ์คํธ์๋ ์ด 7๊ฐ์ ๋ฌธ์ ๊ฐ ์ถ์ ๋์์ผ๋ฉฐ, ๊ฐ๋ฐ ์ธ์ด๋ C++, Java, Jav
tech.kakao.com