[ Algorithm Link ]
최종 코드 미리보기
[ Programmers - sinoso 님 풀이 참고 및 해석 ]
private fun solution(board: Array<IntArray>): Int =
board.indices.map { row ->
board[row].indices.filter { board[row][it] == 1 }.forEach { col ->
((row - 1).coerceAtLeast(0)..(row + 1).coerceAtMost(board.lastIndex)).map { row2 ->
((col - 1).coerceAtLeast(0)..(col + 1).coerceAtMost(board.first().lastIndex)).forEach { col2 ->
if (board[row2][col2] == 0)
board[row2][col2] = 2
}
}
}
}.run { board.indices.map { board[it].count { value -> value == 0 } } }.sum()
1. 각각의 열의 위험지역 위치를 찾는다
board.indices.map { row ->
board[row].indices.filter { board[row][it] == 1}::also { println(it) }
}
// 테스트 케이스 var testC = arrayOf(intArrayOf(0,0,0), intArrayOf(0,1,0), intArrayOf(1,1,1))
// 출력
// []
// [1]
// [0, 1, 2]
2. 지뢰가 영향을 주는 위험지역 범위를 찾는다
board.indices.map { row ->
board[row].indices.filter { board[row][it] == 1}.forEach {col ->
((row - 1).coerceAtLeast(0)..(row + 1).coerceAtMost(board.lastIndex)).map { row2 ->
((col - 1).coerceAtLeast(0)..(col + 1).coerceAtMost(board.first().lastIndex))::also{ println("row2 : $row2 it : $it") }
}
}
}
// 출력
//row : 0 it : 0..2
//row : 1 it : 0..2
//row : 0 it : 0..2
//row : 1 it : 0..2
//row : 2 it : 0..2
//row : 1 it : 0..1
//row : 2 it : 0..1
//row : 1 it : 0..2
//row : 2 it : 0..2
//row : 1 it : 1..2
//row : 2 it : 1..2
- (row - 1).coerceAtLeast(0), ( column + 1)coerceAtMost(board.lastIndex)
- 보드를 시각화 했을 때, 위험지대가 영향을 미치는 세로 열의 범위를 나타낸다. 이때, coerceAtLeast ( 최소치 ), coerceAtMost( 최대치 ) 메서드를 통해서 보드에 존재하지 않는 범위는 제외하도록 처리 하였다.
- (col - 1).coerceAtLest(0), ( column +1 )coerceAtMost(board.first().lastIndex)
- row와 마찬가지로 보드를 시각화 했을 떄, 위험지대가 영향을 미치는 가로 열의 범위를 나타내며 최소치와 최대치를 지정함으로 보드에 존재하지 않는 범위는 제외하도록 처리.
- 위의 과정을 통해 얻고자 하는 solution 리턴 값에 포함되지 않는 위험지대 범위를 알아낼 수 있다.
3. 지뢰를 제외한 위험지역에 값을 2로 할당하고 보드에 0 인 범위만 카운팅 하면 끝
private fun solution(board: Array<IntArray>): Int =
board.indices.map { row ->
board[row].indices.filter { board[row][it] == 1 }.forEach { col ->
((row - 1).coerceAtLeast(0)..(row + 1).coerceAtMost(board.lastIndex)).map { row2 ->
((col - 1).coerceAtLeast(0)..(col + 1).coerceAtMost(board.first().lastIndex)).forEach { col2 ->
if (board[row2][col2] == 0)
board[row2][col2] = 2
}
}
}
}.run { board.indices.map { board[it].count { value -> value == 0 } } }.sum()
첫번째 제출 코드 - 테스트 9번 실패
더보기
[ 제출코드 1 ]
class Solution {
fun solution(board: Array<IntArray>): Int {
var boardLine = mutableListOf<MutableList<MutableList<Int>>>()
for ((index, item) in board.withIndex()) {
var boardSequence = mutableListOf<MutableList<Int>>()
println(board.size - 1)
println("index : $index")
item.mapIndexed { bombIndex, i ->
boardSequence.add(mutableListOf<Int>(bombIndex, i))
}
boardLine.add(boardSequence)
println("boardLines${boardLine}")
}
var bombLocation = mutableListOf<List<List<Int>>>()
for (line in boardLine) {
bombLocation.add(line.filter { it[1] == 1 })
}
println(bombLocation
)
for ((index, item) in bombLocation.withIndex()) {
for ( a in item.indices) {
println("a : $a , item : ${item[a]}")
when (index) {
0 -> {
when (item[a].first()) {
0 -> {
boardLine[index][item[a][0]][1] ++
boardLine[index][item[a][0]+1][1] ++
println("아래 한칸 , 대각선 오른쪽")
boardLine[index+1][item[a][0]][1] ++
boardLine[index+1][item[a][0]+1][1] ++
}
board.size - 1 -> {
println("test ${item[0].size -1}")
boardLine[index][item[a][0]-1][1] ++
boardLine[index][item[a][0]][1] ++
println("아래 한칸 , 대각선 왼쪽")
boardLine[index+1][item[a][0]-1][1] ++
boardLine[index+1][item[a][0]][1] ++
}
else -> {
boardLine[index][item[a][0]-1][1] ++
boardLine[index][item[a][0]][1] ++
boardLine[index][item[a][0]+1][1] ++
println("아래, 대각선 좌,우")
boardLine[index+1][item[a][0]-1][1] ++
boardLine[index+1][item[a][0]][1] ++
boardLine[index+1][item[a][0]+1][1] ++
}
}
println("setSafe ${boardLine[index][item[a][0]][1]}")
println("setSafe ${boardLine[index][item[a][0]]}")
}
board.size - 1 -> {
when (item[a].first()) {
0 -> {
boardLine[index][item[a][0]][1] ++
boardLine[index][item[a][0]+1][1] ++
println("위 한칸 , 대각선 오른쪽")
boardLine[index-1][item[a][0]][1] ++
boardLine[index-1][item[a][0]+1][1] ++
}
board.size - 1 -> {
boardLine[index][item[a][0]-1][1] ++
boardLine[index][item[a][0]][1] ++
println("위 한칸 , 대각선 왼쪽")
boardLine[index-1][item[a][0]-1][1] ++
boardLine[index-1][item[a][0]][1] ++
}
else -> {
boardLine[index][item[a][0]-1][1] ++
boardLine[index][item[a][0]][1] ++
boardLine[index][item[a][0]+1][1] ++
println("위, 대각선 좌,우")
boardLine[index-1][item[a][0]-1][1] ++
boardLine[index-1][item[a][0]][1] ++
boardLine[index-1][item[a][0]+1][1] ++
}
}
}
else -> {
when (item[a].first()) {
0 -> {
boardLine[index][item[a][0]][1] ++
boardLine[index][item[a][0]+1][1] ++
println("아래 한칸 , 대각선 오른쪽")
boardLine[index+1][item[a][0]][1] ++
boardLine[index+1][item[a][0]+1][1] ++
println("위 한칸 , 대각선 오른쪽")
boardLine[index-1][item[a][0]][1] ++
boardLine[index-1][item[a][0]+1][1] ++
}
board.size - 1 -> {
boardLine[index][item[a][0]-1][1] ++
boardLine[index][item[a][0]][1] ++
println("아래 한칸 , 대각선 왼쪽")
boardLine[index+1][item[a][0]-1][1] ++
boardLine[index+1][item[a][0]][1] ++
println("위 한칸 , 대각선 왼쪽")
boardLine[index-1][item[a][0]-1][1] ++
boardLine[index-1][item[a][0]][1] ++
}
else -> {
boardLine[index][item[a][0]-1][1] ++
boardLine[index][item[a][0]][1] ++
boardLine[index][item[a][0]+1][1] ++
println("아래, 대각선 좌,우")
boardLine[index+1][item[a][0]-1][1] ++
boardLine[index+1][item[a][0]][1] ++
boardLine[index+1][item[a][0]+1][1] ++
println("위, 대각선 좌, 우")
boardLine[index-1][item[a][0]-1][1] ++
boardLine[index-1][item[a][0]][1] ++
boardLine[index-1][item[a][0]+1][1] ++
}
}
println("setSafe ${boardLine[index][item[a][0]][1]}")
println("setSafe ${boardLine[index][item[a][0]]}")
}
}
}
}
var answer: Int = (board.size*board.size)
for ( i in boardLine) {
for ( j in i) {
if ( j[1] >= 1) {
answer--
}
}
}
println("answer : ${answer}")
return answer
}
}
[ 반려 사유 ]
코드 실행은 넘어가지만, 테스트 9 번 실패
[ 원인 ]
- 문제1. board.size가 1일 경우의 런타임 오류 발생
- 테스트 9번은 board의 size가 1일 경우이다
제출코드 - 통과 → 리팩토링 전
class Solution {
fun solution(board: Array<IntArray>): Int {
if (board.size ==1 && board[0][0] == 0) {
return 1
} else if (board.size ==1 && board[0][0] == 1) {
return 0
} else {
// 제출코드 1의 코드
}
}
[ 작성코드 정리 ]
- 초기에 작성 코드 구상 시, 보드의 넓이 ( n * n ) 의 구석자리 ( 0 , n - 1( lastIndex라고 얘기할 것. ) ) 에 위치한 인덱스는 각각 음수 인덱스, n의 크기를 초과하는 인덱스로 영향을 끼치지 못하게 when으로 0, lastIndex, else로 구분하여 노가다로 작성하기로 했다.
- 1. 보드의 각 열에서 0, lastIndex, else로 구분하였으면, 첫번째 열은 맨 아래 열이므로 해당 열의 아래칸에 접근을 시도하지 않고, 마찬가지로 lastIndex는 맨 위에 열이므로 해당 열의 위 칸에 접근을 시도하지 않게 하여서 런타임 오류가 나지 않도록 했다
- 2. 각 열 안에 0, lastIndex도 맨 왼쪽, 맨 오른쪽에 해당 하는 위치기 때문에 보드에 존재하지 않는 인덱스에 접근하지 않도록 했다.
- 1, 2 의 노가다 과정에서 코드가 많이 작성될 것 같아 간소화할 수 있는 방법을 생각해보았는데 당장 떠오르는게 없어 일단 진행하고, 완성한 후 추가 학습을 해보려고 했다.
- [ 제출코드 1 ] 에서 board.size == 1인 상황에 런타임이 나기에 board.size가 1일 경우의 예외처리를 추가하니 통과가 되었다.
알고리즘 회고
- 알고리즘을 푸는 도중에 다른 풀이 방법들이 이따금씩 떠올랐는데, 기존 틀을 깨버리고 다시 만들다가 실패하게 되었다. 그제서야 백업해둔 것이 없어 브랜치에서 작업하는것이 중요하다는 것을 느꼈다.
- bombLocation을 변수로 선언한 이유는 위험지역에 해당하는 인덱스의 값을++ 을 하기 때문에 위험 지역으로 끝나야 할 인덱스 범위가 반복문을 돌다보면 지뢰와 같이 인식되어 범위가 확장되기 때문에 초기 위치로만 반복문을 돌도록 작성하기 위함인데, 위험지역을 ++ 하는 방법 말고 2처럼 다른 값을 할당하고, 지뢰가 위치한 곳은 1로 두면 초기 지뢰 위치를 굳이 bombLocation으로 찾을 필요가 없다는 것을 끝나고나서야 깨달아서 되게 허탈했다.
[ 새롭게 배운 것 ]
- collection.size 는 컬렉션 안에 요소의 갯수가 다 나오고, collection.lastIndex는 0부터 시작하는 총 갯수기 때문에 .size -1 과 동일한 결과가 나온다. 가독성도 lastIndex가 더 좋은 것 같아. 원소의 총 갯수와 마지막 인덱스를 구분해서 사용할 것이다
[Reference]
'Algorithm' 카테고리의 다른 글
[Algorithm - Stack] Kotlin - programmers Lv1 [ 크레인 게임 ] (0) | 2024.04.23 |
---|---|
[Algorithm - Unicode] Kotlin - programmers Lv1 [ 시저 암호 ] (0) | 2024.04.16 |
[Algorithm] Kotlin / Programmers Lv0 [ 평행 ] (0) | 2024.04.06 |
[Algorithm] Kotlin - programmers lv0 [ 겹치는 선분의 길이 ] - 테스트 1, 9번 오류 해결 (0) | 2024.03.15 |
[Algorithm] Kotlin - 프로그래머스 입문 [ 연속된 수의 합 ] (0) | 2024.03.10 |