itertools
로 combinations
불러와서 조합의 개수를 구하려다가 이렇게 간단하게 해결 될 문제가 아닌 것 같아서 defaultdict
를 사용하는 방법을 찾았다.[["yellow_hat", "headgear"], ["blue_sunglasses", "eyewear"], ["green_turban", "headgear"]]
에서 headgear
, eyewear
의 개수를 세주기 위해 defaultdict
를 사용했다.key
로 가지고, 그 개수를 value
로 가지게 한다.개수를 구할 때 규칙을 찾는 것이 중요하다.
첫번째 입력 예시에서, 이를 defaultdict
로 만들고 개수를 value
로 가지게 하면 {'headgear': 2, 'eyewear': 1}
이런 딕셔너리가 생성된다.
같은 종류의 옷은 2벌이상 입지 못하므로, 여러 벌이 있더라도 한 벌만 선택할 수 있다. 예시의 경우 모자는 2C1
개 선택이 가능하다. 또, 아예 선택하지 않는 경우인 2C0
이 있다. 입고 나갈 모자를 고르는 경우의 수는 2C1+2C0
인 것이다.
안경의 경우 한가지 종류만 존재하므로 입거나(1C1
) 안입거나(1C0
)이 두가지 경우가 존재한다. 안경을 고르는 경우의 수는 1C1+1C0
인 것이다.
그렇다면 입고 나갈 옷을 고르는 경우의 수는 총 (2C1+2C0) * (1C1+1C0)
개가 된다. 하지만 문제의 조건에서 스파이는 하루에 최소 한 개의 의상은 입습니다.
라고 했으므로 옷을 안 입는 경우를 빼줘야한다. 저기서 1만 빼주면 됨.
두번째 입력 예시를 보면 [["crow_mask", "face"], ["blue_sunglasses", "face"], ["smoky_makeup", "face"]]
이와 같은데, 이 경우 {'face': 3}
이런 딕셔너리가 생성된다. 역시 얼굴에 착용하는 것도 하나만 고를 수 있으므로 3C1
개의 경우의 수가 존재하고, 아예 착용하지 않는 경우의 수인 3C0
도 존재한다. 근데 최소 한 개의 의상은 입으니 이 경우도 총 경우의 수에서 1을 빼주면 된다.
이걸 바탕으로 식을 일반화 하는 것이 중요한 포인트인 것 같다.
{'옷 종류 1': n, '옷 종류2': m, '옷 종류3': k, '옷 종류4': p}
라고 했을 때, 서로 다른 옷의 조합의 수는 (nC1+nC0) * (mC1+mC0) * (kC1+kC0) * (pC1+pC0) -1
이 된다.
이를 바탕으로 코드를 짜주면 해결!
from collections import defaultdict
def solution(clothes):
clothings = defaultdict(int)
answer = 1
for cloth in clothes:
clothings[cloth[1]] += 1
for clothing in clothings.values():
answer *= (clothing+1)
return answer-1
규칙만 찾으면 코드 짜는 것은 어렵지 않은 문제이다.
defaultdict
딕셔너리에서 키값을 지정해주지 않고 호출할 경우 키에러가 뜬다.
하지만 defaultdict
를 이용해주면 호출과 동시에 미리 지정한 기본값이 할당되도록 할 수 있다.
defaultdict
를 활용해 다음과 같이 기본 값을 int
로 선언해주고,
from collections import defaultdict
d_dict = defaultdict(int)
기존에 없던 키를 호출하면 해당 키가 0으로 자동 초기화된다.
d_dict['new'] # 출력 0
d_dict
를 단독 호출하면 defaultdict(<class 'int'>, {'new': 0})
이와 같이 출력된다. 기본적인 활용은 당연히 딕셔너리니까 똑같이 .values()
등을 사용해 줄 수 있다.
기본 값으로 리스트도 넣어줄 수 있고 람다식을 이용해 원하는 초기 값을 지정해주는 것도 가능하다.
Counter
, reduce
)def solution(clothes):
from collections import Counter
from functools import reduce
cnt = Counter([kind for name, kind in clothes])
answer = reduce(lambda x, y: x*(y+1), cnt.values(), 1) - 1
return answer
코드를 하나씩 뜯어보며 사용된 모듈에 대해 설명하고자 한다.
Counter
Counter
를 사용하면 중복된 데이터가 저장된 배열을 인자로 넘기면 각 원소가 몇 번씩 나오는지가 저장된 객체 를 얻게된다. 내가 defaultdict
를 사용한 과정을 좀 더 단축할 수 있는..!!
cnt = Counter([kind for name, kind in clothes])
리스트 컴프리헨션을 이용해서 clothes
의 1번째 인덱스인 옷종류 kind
를 리스트에 담아주고, 거기서 옷 종류의 개수를 Counter
로 계산한다.
첫번째 예시로 들면 리스트 안엔 ['headgear', 'eyewear', 'headgear']
이렇게 담기는 것이다.
Counter
를 사용하면 Counter({'headgear': 2, 'eyewear': 1})
를 반환한다.
reduce
파이썬의 functools
내장 모듈이다. reduce()
는 여러 개의 데이터를 대상으로 주로 누적 집계를 내기 위해 사용한다. 내가 반복문을 써서 답을 구한 것을 한줄로 구현할 수 있게 해준다!
reduce(집계 함수, iterable한 객체, 초기 값)
문제에서는 lambda
로 집계함수를 나타낸 것이다. 이 코드는 lambda로 집계함수를 나타낸 것이다.
lambda x,y : x*(y+1)
은 x
값에 y+1
을 곱한다는 뜻으로 내 코드에서 반복문을 돌려가며 answer *= (clothing+1)
를 해줬던 것과 같은 기능이다. y
는 초기값 1부터 시작하고, 순회 가능한 데이터인 cnt.values()
로 y
값을 변경시켜 준다. 이렇게 하면 반복문을 사용하지 않고 누적 집계를 낼 수 있다!!
마지막에 옷을 한 벌도 입지 않는 경우를 하나 빼주면 답이 나온다.
def solution(clothes):
clothes_type = {}
for c, t in clothes:
if t not in clothes_type:
clothes_type[t] = 2
else:
clothes_type[t] += 1
cnt = 1
for num in clothes_type.values():
cnt *= num
return cnt - 1
모듈을 최소화하고 해시를 구현한 코드이다!!
{key: value}
)이 문제에서는 키를 옷의 종류로 두고, 데이터가 옷의 개수인 것이다.
해시를 사용하는 문제를 마주했을 때 키와 데이터를 어떻게 줄 지 결정하는 것이 제일 중요할 것 같다.