1️⃣ 문제

  • 크기가 무한인 정사각형 모눈종이가 있다. 모눈종이의 각 정사각형은 행과 열의 쌍으로 표현할 수 있다.

    이 모눈종이 전체를 양의 정수의 소용돌이 모양으로 채울 것이다. 일단 숫자 1을 0행 0열에 쓴다. 그리고 나서 0행 1열에 숫자 2를 쓴다. 거기서 부터 소용돌이는 반시계 방향으로 시작된다. 다음 숫자는 다음과 같이 채우면 된다.

        -3 -2 -1  0  1  2  3
        --------------------
    -3 |37 36 35 34 33 32 31
    -2 |38 17 16 15 14 13 30
    -1 |39 18  5  4  3 12 29
     0 |40 19  6  1  2 11 28
     1 |41 20  7  8  9 10 27
     2 |42 21 22 23 24 25 26
     3 |43 44 45 46 47 48 49
    

    이 문제는 위와 같이 채운 것을 예쁘게 출력하면 된다. r1, c1, r2, c2가 입력으로 주어진다. r1, c1은 가장 왼쪽 위 칸이고, r2, c2는 가장 오른쪽 아래 칸이다.

    예쁘게 출력한다는 것은 다음과 같이 출력하는 것이다.

    1. 출력은 r1행부터 r2행까지 차례대로 출력한다.
    2. 각 원소는 공백으로 구분한다.
    3. 모든 행은 같은 길이를 가져야 한다.
    4. 공백의 길이는 최소로 해야 한다.
    5. 모든 숫자의 길이(앞에 붙는 공백을 포함)는 같아야 한다.
    6. 만약 수의 길이가 가장 길이가 긴 수보다 작다면, 왼쪽에서부터 공백을 삽입해 길이를 맞춘다.

    입력

    첫째 줄에 네 정수 r1, c1, r2, c2가 주어진다.

    출력

    r2 - r1 + 1개의 줄에 소용돌이를 예쁘게 출력한다.

    제한

    • -5 000 ≤ r1, c1, r2, c2 ≤ 5,000
    • 0 ≤ r2 - r1 ≤ 49
    • 0 ≤ c2 - c1 ≤ 4

2️⃣ 풀이 방법

i, j에 따른 소용돌이 값 구하기

해당하는 범위의 모든 값들을 배열에 할당하면 메모리 초과가 뜨기 때문에, 그때 그때 필요한 value를 구할 수 있는 함수를 이용하였다.

아래 그림을 보면 우측하단 꼭지점인 (n, n)에 해당하는 값은 (2n+1)^2임을 알 수 있고, 시계 방향으로 다음 꼭지점은 이전 꼭지점 대비 2n만큼 감소한다.

이를 통해 공식을 도출할 수 있다.

자릿수에 따른 공백 처리

10으로 나눈 나눗셈 값이 0이 될 때까지 재귀 호출하여 depth를 반환하면 자릿수를 구할 수 있다.

소용돌이 출력 시 max_자리수 - 소용돌이 자리수 값만큼 공백을 추가해주었다.

3️⃣ 코드

import Foundation

// 소용돌이 value get 함수
func getSwirlValue(_ i: Int, _ j: Int) -> Int {
    let n = max(abs(i), abs(j))
    var val = (pow(Decimal(2 * n + 1), 2) as NSDecimalNumber).intValue   // n*n 크기의 소용돌이에서 우측 하단 값
    
    let diff = 2 * n    // 모서리별 값 차이
    if i == n { return  val - (n - j) }
    val -= diff
    if j == -n { return  val - (n - i) }
    val -= diff
    if i == -n { return  val - (j + n) }
    val -= diff
    return  val - (i + n)
}

// 몇자리 수인지 get하는 함수
func getDecimalCount(_ val: Int) -> Int {
    return val != 0 ? getDecimalCount(val / 10) + 1: 0
}

let input = readLine()!.split(separator: " ").map { Int($0) }

let maxIndex = input.max { $0! < $1! }

// 최대 자릿수 찾기
var maxDC = 0
for i in  input[0]!...input[2]! {
    for j in input[1]!...input[3]! { maxDC = max(getDecimalCount(getSwirlValue(i, j)), maxDC) }
}

// 소용돌이 출력하기
for i in  input[0]!...input[2]! {
    for j in input[1]!...input[3]! {
        let interval = maxDC - getDecimalCount(getSwirlValue(i, j))
        let space = [String](repeating: " ", count: interval).joined()
        print("\(space)\(getSwirlValue(i, j))", terminator: " ")
    }
    print("", terminator: "\n")
}

4️⃣ 이 문제를 풀면서 되새긴 점

playground에서는 readLine()이 먹히지 않는다. nil만 뜸.

  • 백준 문제풀 때는 command lines tool Xcode 프로젝트 생성해서 풀기!!

공백을 기준으로 입력 받는 법

  • 반환 타입이 옵셔널 String이기 때문에, .map() 함수로 꼭 원하는 형태로 변환해서 쓰면 된다.
let input = readLine()!.split(separator: " ")

개행없이 print() 하는 법

  • swift는 이럴땐 또 친절해서 알아서 개행을 해줌. 이를 terminator로 출력 원소 간 간격 조절? 가능
print(n, terminator: " ")
print("\n")	// 이렇게 쓰면 두번 개행하게 됨. 한번만은 그냥 print("")