⚙️ 알고리즘/문제풀이

[Swift] 백준 1913, 프로그래머스 베스트앨범

dev_zoe 2025. 5. 7. 23:15
반응형

1. 문제: https://www.acmicpc.net/problem/1913

✅ 풀이

let n = Int(readLine()!)!
let m = Int(readLine()!)!

var arr = Array(repeating: Array(repeating: 0, count: n), count: n)

// 하우상좌 오프셋 (달팽이가 회전하면서 수가 채워지는 순서)
let dx = [1, 0, -1, 0]
let dy = [0, 1, 0, -1]

var dir = 0
var num = n * n
var x = 0, y = 0

var targetX = 0, targetY = 0

while num > 0 {
    arr[x][y] = num
    if num == m {   // 문제에서 찾고자하는 target의 좌표 저장
        targetX = x + 1
        targetY = y + 1
    }

    var nx = x + dx[dir]
    var ny = y + dy[dir]

    if !(0 <= nx && nx < n && 0 <= ny && ny < n && arr[nx][ny] == 0) {  // 범위에 맞지않거나 이미 채운곳이라면
        dir = (dir + 1) % 4
        nx = x + dx[dir]
        ny = y + dy[dir]
    }

    x = nx
    y = ny
    num -= 1
}

// 출력
for row in arr {
    print(row.map { String($0) }.joined(separator: " "))
}
print("\(targetX) \(targetY)")

 

2. 문제: https://www.acmicpc.net/problem/1913

✅ 풀이

import Foundation

func solution(_ genres:[String], _ plays:[Int]) -> [Int] {
    var totalDic: [String:Int] = [:]
    var playDic: [String: [(Int, Int)]] = [:]
    var answer:[Int] = []
    
    for (index, genre) in genres.enumerated() {
        playDic[genre, default:[]].append((index, plays[index]))  // 장르별 (고유번호, 재생수)
        totalDic[genre, default:0] += plays[index]   // 장르별 총 재생수
    }
    
    let sortedTotalDic = totalDic.sorted(by: { $0.1 > $1.1 })   // 가장 많이 재생된 장르 순서대로 정렬
    
    for (k, v) in sortedTotalDic {
        let sortedPlayDic = playDic[k]!.sorted {
            if $0.1 == $1.1 {     // 재생 횟수가 같으면 고유 번호가 낮은 노래 먼저 수록
                return $0.0 < $1.0
            } else {              // 장르 내에서 많이 재생된 노래를 먼저 수록
                return $0.1 > $1.1
            }
        }
        if sortedPlayDic.count >= 2 {    // 2개까지 수록 가능, 1개라면 1개 추가
            answer.append(sortedPlayDic[0].0)
            answer.append(sortedPlayDic[1].0)
        } else {
            answer.append(sortedPlayDic[0].0)
        }
    }
    
    return answer
}

 

✅ 최적화된 풀이

import Foundation

func solution(_ genres:[String], _ plays:[Int]) -> [Int] {
    var totalDic: [String:Int] = [:]
    var playDic: [String: [(Int, Int)]] = [:]
    var answer:[Int] = []
    
    for (index, genre) in genres.enumerated() {
        playDic[genre, default:[]].append((index, plays[index]))  // 장르별 (고유번호, 재생수)
        totalDic[genre, default:0] += plays[index]   // 장르별 총 재생수
    }
    
    let sortedTotalDic = totalDic.sorted(by: { $0.1 > $1.1 })   // 가장 많이 재생된 장르 순서대로 정렬
    
    for (k, v) in sortedTotalDic {
        let sortedPlayDic = playDic[k]!.sorted {
            if $0.1 == $1.1 {     // 재생 횟수가 같으면 고유 번호가 낮은 노래 먼저 수록
                return $0.0 < $1.0
            } else {              // 장르 내에서 많이 재생된 노래를 먼저 수록
                return $0.1 > $1.1
            }
        }.prefix(2)   // 최대 2개까지 수록 가능
        answer.append(contentsOf: sortedPlayDic.map { $0.0 })    // 고유 번호 추가
    }
    return answer
}

 

- prefix(2) 를 통해 최대 2개까지의 배열을 반환한다.

- cotentsOf: 를 통해 기존 answer 배열에 배열의 원소들을 펼쳐서 추가해준다.

 

오늘 알게 된 점

1. Swift 반복문에서 거꾸로 순회하는 방법

for i in stride(from: a, to: b, by: -c) == Python에서 for i in range(a, b, -c) 와 같다.

 

2. 배열, 좌표, 행렬 이동 문제는 규칙을 찾아 dx, dy 오프셋을 활용하여 문제를 풀면 편리하다.

 

3. 딕셔너리 관련 Swift 코드

// 0) Swift의 딕셔너리에서 key-value 쌍이 없으면 nil을 반환한다.
// 1) 기본값을 지정하여 계산하기
dic[stage, default:0] += 1    // key에 해당하는 value가 없다면 먼저 0을 초기화하고, 1을 더함

// 2) 반복문을 통해 딕셔너리 접근하는 방법
for (key, value) in dic {
    print(key, value)
}

// 3-1) 딕셔너리 key값 기준으로 정렬
let sortedDic = dic.sorted(by: { $0.0 < $1.0 })

// 3-2) 딕셔너리 value값 기준으로 정렬
let sortedDic = dic.sorted(by: { $0.1 < $1.1 })

// 3-3) 2가지 기준 이상으로 정렬 (value값이 같으면 key값을 사전순으로, 그게 아니라면 key값 내림차순)
let sortedDic = dic.sorted { 
	if $0.value == $1.value {   // 같을때 어떻게 해야하는지의 기준을 먼저 적으면 덜헷갈림
    	return $0.key < $1.key
    }
    return $0.key > $1.key
}

 

4. Swift에서 정수를 나누어 소수점으로 반환할때, Double(Int / Int)가 아니라 Double(Int) / Double(Int)로 나누도록 주의한다.

 

5. enumerated(): 인덱스와 value값을 함께 접근하고 싶을때

let str = "ABCDE"
for (i, e) in str.enumerated() {
    print("\(i) - \(e)")
}

// 위 코드와 아래 코드는 아래와 같이 출력 동일
for tuple in str.enumerated() {
	print("\(tuple.0) - \(tuple.1)")
}

// 0 - A
// 1 - B
// 2 - C
// 3 - D
// 4 - E

for tuple in str.enumerated().reversed() { // 거꾸로 인덱스, 요소 차례대로 접근
	print("\(tuple.0) - \(tuple.1)")
}
// 4 - E
....
// 0 - A

 

6. 배열 -> 문자열 출력: [배열].joined(separator:"구분자")

7. append(contentsOf: Array): 배열 끝에 인자의 array element를 추가하는 메소드

8. prefix(n)과 suffix(n)은 기존 원본 배열/문자열이 n보다 길이가 짧아도 오류가 발생하지 않는다. 알아서 최대 갯수로 잘라서 가져와주는 똑똑한 친구임

9. map, flatMap을 적극 활용하자.

반응형