[알고리즘] 의상 풀이

박주하·2025년 6월 11일

🔍 문제 설명


코니는 매일 다른 옷을 조합하여 입는것을 좋아합니다.

예를 들어 코니가 가진 옷이 아래와 같고, 오늘 코니가 동그란 안경, 긴 코트, 파란색 티셔츠를 입었다면 다음날은 청바지를 추가로 입거나 동그란 안경 대신 검정 선글라스를 착용하거나 해야합니다.

종류이름
얼굴동그란 안경, 검정 선글라스
상의파란색 티셔츠
하의청바지
겉옷긴 코트
  • 코니는 각 종류별로 최대 1가지 의상만 착용할 수 있습니다. 예를 들어 위 예시의 경우 동그란 안경과 검정 선글라스를 동시에 착용할 수는 없습니다.
  • 착용한 의상의 일부가 겹치더라도, 다른 의상이 겹치지 않거나, 혹은 의상을 추가로 더 착용한 경우에는 서로 다른 방법으로 옷을 착용한 것으로 계산합니다.
  • 코니는 하루에 최소 한 개의 의상은 입습니다.

코니가 가진 의상들이 담긴 2차원 배열 clothes가 주어질 때 서로 다른 옷의 조합의 수를 return 하도록 solution 함수를 작성해주세요.


제한사항

  • clothes의 각 행은 [의상의 이름, 의상의 종류]로 이루어져 있습니다.
  • 코니가 가진 의상의 수는 1개 이상 30개 이하입니다.
  • 같은 이름을 가진 의상은 존재하지 않습니다.
  • clothes의 모든 원소는 문자열로 이루어져 있습니다.
  • 모든 문자열의 길이는 1 이상 20 이하인 자연수이고 알파벳 소문자 또는 '_' 로만 이루어져 있습니다.

입출력 예

clothesreturn
[["yellow_hat", "headgear"], ["blue_sunglasses", "eyewear"], ["green_turban", "headgear"]]5
[["crow_mask", "face"], ["blue_sunglasses", "face"], ["smoky_makeup", "face"]]3

입출력 예 설명

예제 #1

headgear에 해당하는 의상이 yellow_hat, green_turban이고 eyewear에 해당하는 의상이 blue_sunglasses이므로 아래와 같이 5개의 조합이 가능합니다.

1. yellow_hat
2. blue_sunglasses
3. green_turban
4. yellow_hat + blue_sunglasses
5. green_turban + blue_sunglasses

예제 #2

face에 해당하는 의상이 crow_mask, blue_sunglasses, smoky_makeup이므로 아래와 같이 3개의 조합이 가능합니다.

1. crow_mask
2. blue_sunglasses
3. smoky_makeup

✍️ 문제 풀이


  1. Dictionary 사용: 의상 종류별 개수를 딕셔너리로 정리하기
  2. 경우의 수 계산
    • 각 종류의 의상은 1개 이상 선택하지 않아도 되지만, 전체 아무것도 입지 않는 경우는 제외하기
    • 모든 종류에 대해 (해당 종류의 아이템 개수 + 1(착용 안하는 경우))을 곱한 후 - 1(모든 종류에서 아무것도 선택하지 않은 경우)

      💡 예

      • [["yellow_hat", "headgear"], ["blue_sunglasses", "eyewear"], ["green_turban", "headgear"]]
      • headgear: 2개, eyewear: 1개
        → 총 경우의 수: (2+1) * (1+1) - 1 = 5

🤯 개선 전: 처음 작성한 코드

  • typeCount 딕셔너리
    • "의상 종류"를 키로, "각 종류의 의상 개수"를 값으로 저장
    • 예: ["headgear": 2, "eyewear": 1]
  • contains + ! 사용
    • 딕셔너리에 키(의상 종류)가 있는지 확인한 후, 있으면 값을 증가, 없다면 새로 1을 넣음
    • !를 사용해 강제 언래핑
  • reduce + 클로저
    • 딕셔너리 순회하며 (value + 1)로 각 종류별 선택지 수를 구하고,
    • reduce(1)로 곱해서 총 조합 수 계산 후,
    • -1로 아무것도 착용하지 않는 경우를 제외
import Foundation

func solution(_ clothes:[[String]]) -> Int {
    var typeCount = [String: Int]()
    
    // 종류별 개수 카운팅
    for array in clothes {
        if typeCount.contains { $0.0 == array[1] } {
            typeCount[array[1]]! += 1
        } else {
            typeCount[array[1]] = 1
        }
    }
    
    // 경우의 수 계산
    return typeCount.reduce(1) { $0 * ($1.value + 1) } - 1
}

🤩 개선된 코드

  • default: 사용
    • Swift의 딕셔너리 기본 기능으로 코드 간결화
    • ! 사용은 지양, 훨씬 안전한 코드 작성
    • 딕셔너리에 키(의상 종류)가 없다면 기본값 0을 사용해서 1 증가, 있다면 기존 값에 1 증가
  • 코드의 가독성 + 안전성 향상
import Foundation

func solution(_ clothes:[[String]]) -> Int {
    var typeCount = [String: Int]()
	
    // 종류별 개수 카운팅
    for cloth in clothes {
        typeCount[cloth[1], default: 0] += 1
    }
	
    // 경우의 수 계산
    return typeCount.reduce(1) { $0 * ($1.value + 1) } - 1
}

📌 비교 정리

항목개선 전개선 후
딕셔너리 값 증가contains + !default: 사용
가독성낮음더 명확하고 간결
안전성!로 crash 위험 있음안전하게 처리 가능
성능contains는 느릴 수 있음 (O(n))딕셔너리 직접 접근하여 빠름 (O(1))

👨🏻‍💻 튜터님 코드

  • 깔끔해서 가독성이 좋고, 의도를 명확하게 표현
func solution(_ clothes: [[String]]) -> Int {
  return clothes
    .reduce(into: [:]) { $0[$1[1], default: 0] += 1 } // 의상 분류
    .values
    .map { $0 + 1 } // 해당 종류를 입지 않은 경우의 수 +1
    .reduce(1, *) - 1 // (모든 경우의 수) - 1(아무것도 입지 않은 경우의 수)
}

💡 문제 출처
https://school.programmers.co.kr/learn/courses/30/lessons/42578

0개의 댓글