알고리즘/문제풀이

[백준/Gold4] Swift - 주사위 굴리기

피자식빵 2025. 6. 22. 14:15

2025.06.22 기준 Gold4
https://www.acmicpc.net/problem/14499


알고리즘

구현, 시뮬레이션

문제 요약

  • 크기가 N×M인 2차원 지도 위에 주사위가 놓여 있다.
  • 주사위의 초기 위치는 (x, y)이고, 이동 명령이 순서대로 주어진다.
  • 주사위는 지도 바깥으로는 나갈 수 없으며, 나가려고 하면 그 명령은 무시된다.
  • 주사위를 굴릴 때마다 윗면에 적힌 숫자를 출력해야 한다.
  • 지도의 좌표는 (r, c)로 나타내며, r는 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로부터 떨어진 칸의 개수이다.

주사위 전개도

    2
  4 1 3
    5
    6

 

  • 가장 처음에 주사위에는 모든 면에는 0이 적혀져 있다. 
  • 1이 윗면, 3은 동쪽을 바라보도록 놓여있다.

 지도 규칙

  • 주사위가 이동한 칸이 0이라면, 주사위 바닥면의 숫자를 해당 칸에 복사한다.
  • 0이 아니라면, 해당 칸의 숫자를 주사위 바닥면에 복사하고, 해당 칸은 0으로 바뀐다.
  • 주사위를 놓은 칸에 쓰여 있는 수는 항상 0이다.
  • 지도의 각 칸에 쓰여 있는 수는 10 미만의 자연수 또는 0이다.

 

명령어는 최대 1,000개가 주어지며, 각 숫자의 의미는 다음과 같다:

  • 1: 동쪽
  • 2: 서쪽
  • 3: 북쪽
  • 4: 남쪽

 

제약 조건

첫 줄: N, M, x, y, K

다음 N줄: 지도 정보 (각 줄 M개 숫자)

마지막 줄: 명령어 K개

  • N: 세로크기 (1 ≤ N ≤ 20)
  • M: 가로 크기 (1 ≤ M ≤ 20)
  • x, y: 주사위를 놓은 곳의 좌표 (0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1)
  • K: 명령어 개수 (1 ≤ K ≤ 1,000)

 

 

입출력 예

// 입력
4 2 0 0 8
0 2
3 4
5 6
7 8
4 4 4 1 3 3 3 2

// 출력
0
0
3
0
0
8
6
3

 

// 입력
2 2 0 0 16
0 2
3 4
4 4 4 4 1 1 1 1 3 3 3 3 2 2 2 2

// 출력
0
0
0
0

 

아이디어

2초의 시간 제한이 있지만, 명령어의 최대 개수가 1000개이기 때문에 요구사항을 잘 구현하기만 하면 된다고 생각함.

 

 

1. 주사위 굴리는 범위 확인

지도 바깥으로 나가면 모두 무시되기 때문에 가장 먼저 주사위를 굴릴 수 있는 범위인지 확인

  • 주사위를 굴릴 수 있다면 현재 위치 업데이트 및 true 반환
  • 주사위를 굴릴 수 없다면 false 번환
func diceCheck(_ command: Int) -> Bool {
    var y = diceY
    var x = diceX
    switch command {
    case 1:
        x += 1
    case 2:
        x -= 1
    case 3:
        y -= 1
    case 4:
        y += 1
    default:
        break
    }
    
    if (0..<height).contains(y) && (0..<width).contains(x) {
        diceY = y
        diceX = x
        return true
    } else {
        return false
    }
}

 

 

2. 주사위 굴리기

주사위를 굴렸을 때, 하단과 상단의 값을 관리하는 배열을 생성

 

 

 

주어진 전개도를 기준으로 주사위를 굴렸을 때 상태 변화 확인 후

만든 배열의 값을 수정하는 diceRoll이라는 함수 구현

func diceRoll(_ command: Int) -> [Int] {
    var temp = diceInfo     // 1: 상 2: 하 3: 동 4: 서 5: 북 6: 남
    switch command {
    case 1:     // 동쪽
        temp[1] = diceInfo[4]
        temp[2] = diceInfo[3]
        temp[3] = diceInfo[1]
        temp[4] = diceInfo[2]
    case 2:     // 서쪽
        temp[1] = diceInfo[3]
        temp[2] = diceInfo[4]
        temp[3] = diceInfo[2]
        temp[4] = diceInfo[1]
    case 3:     // 북쪽
        temp[1] = diceInfo[6]
        temp[2] = diceInfo[5]
        temp[5] = diceInfo[1]
        temp[6] = diceInfo[2]
    case 4:     // 남쪽
        temp[1] = diceInfo[5]
        temp[2] = diceInfo[6]
        temp[5] = diceInfo[2]
        temp[6] = diceInfo[1]
    default:
        break
    }
    return temp
}

 

 

3. 명령어 수행

이 후 명령어에 따라 주사위를 굴리고, 이동한 위치의 값이 0인지 아닌지에 따라 구현

 

 

풀이

// 주사위 굴리기, 구현

import Foundation

let input = readLine()!.split(separator: " ").map{Int(String($0))!}
let height = input[0], width = input[1]
var diceY = input[2], diceX = input[3]
let command = input[4]

var maps: [[Int]] = []
for _ in 0..<height {
    maps.append(readLine()!.split(separator: " ").map{Int(String($0))!})
}

let commands = readLine()!.split(separator: " ").map{Int(String($0))!}
var dices = [0, 0, 0, 0, 0, 0, 0]
var diceInfo = [0, 1, 6, 3, 4, 2, 5] // 상단, 하단, 동쪽, 서쪽, 북쪽, 남쪽

func diceCheck(_ command: Int) -> Bool {
    var y = diceY
    var x = diceX
    switch command {
    case 1:
        x += 1
    case 2:
        x -= 1
    case 3:
        y -= 1
    case 4:
        y += 1
    default:
        break
    }
    
    if (0..<height).contains(y) && (0..<width).contains(x) {
        diceY = y
        diceX = x
        return true
    } else {
        return false
    }
}

func diceRoll(_ command: Int) -> [Int] {
    var temp = diceInfo     // 1: 상 2: 하 3: 동 4: 서 5: 북 6: 남
    switch command {
    case 1:     // 동쪽
        temp[1] = diceInfo[4]
        temp[2] = diceInfo[3]
        temp[3] = diceInfo[1]
        temp[4] = diceInfo[2]
    case 2:     // 서쪽
        temp[1] = diceInfo[3]
        temp[2] = diceInfo[4]
        temp[3] = diceInfo[2]
        temp[4] = diceInfo[1]
    case 3:     // 북쪽
        temp[1] = diceInfo[6]
        temp[2] = diceInfo[5]
        temp[5] = diceInfo[1]
        temp[6] = diceInfo[2]
    case 4:     // 남쪽
        temp[1] = diceInfo[5]
        temp[2] = diceInfo[6]
        temp[5] = diceInfo[2]
        temp[6] = diceInfo[1]
    default:
        break
    }
    return temp
}

for c in commands {
    if diceCheck(c) {               // 명령어가 유효하면
        diceInfo = diceRoll(c)      // 주사위 굴리기
        let diceBottom = diceInfo[2]
        if maps[diceY][diceX] == 0 {    // 이동한 칸이 0일때, 주사위 바닥면 수를 복사
            maps[diceY][diceX] = dices[diceBottom]
        } else {                        // 그렇지 않으면 이동 칸 숫자를 주사위 바닥면에 복사
            dices[diceBottom] = maps[diceY][diceX]
            maps[diceY][diceX] = 0
        }
        
        print(dices[diceInfo[1]])
    }
}
 

 

개선점

dy, dx 배열을 사용하여 diceCheck를 단순화 할 수 있음

let dy = [0, 0, 0, -1, 1]                           // 동, 서, 북, 남 순
let dx = [0, 1, -1, 0, 0]
 

 

실수 포인트

이동한 칸이 0이 아닐 때, 주사위 바닥면에 값을 복사하고 해당 칸은 0이 된다는 조건을 놓침