레이블이 2023 KAKAO BLIND RECRUITMENT인 게시물을 표시합니다. 모든 게시물 표시
레이블이 2023 KAKAO BLIND RECRUITMENT인 게시물을 표시합니다. 모든 게시물 표시

2024년 4월 21일 일요일

2022 KAKAO BLIND RECRUITMENT 신고 결과 받기 Lv. 1

문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/92334

파이썬 소스: https://bit.ly/3UAYKWR

 

문제를 풀기에 앞서서, solution()함수의 인자를 report에서 reports로 변경했습니다. 특별한 이유는 없고, 리스트변수라서 복수의 s를 붙여 줬습니다.

def solution(id_list: List[str], reports: List[str], k: int):

 

문제에서 주의해야 할 부분이 아래 부분입니다.

한 유저를 여러 번 신고할 수도 있지만, 동일한 유저에 대한 신고 횟수는 1회로 처리됩니다.

이 부분은 set()을 사용하면 쉽게 구현할 수 있습니다.

신고한 유저 reporter와 신고된 유저 reported가 있다고 하면, reported에 대해서 set()을 만들고 아래처럼, reported를 신고한 유저를 set()에 계속 추가할 수 있습니다. set()에는 중복된 것이 들어오면 자동으로 삭제해줍니다.

 신고된 유저도 여러 명이 있을 수 있으므로, defaultdict()를 사용해서 아래와 같이 구현할 수 있습니다.

dict = defaultdict(set)

for report in reports:
    reporter, reported = report.split(
' ')
    dict[reported].add(reporter)

신고한 유저와, 신고된 유저를 reporter, reported변수로 각각 나눕니다.

딕셔너리는 reported를 키로 set()을 하나씩 가지고 있기 때문에, reported를 신고한 유저를 add()메서드로 추가해 줍니다.

 

신고한 유저의 숫자가 k 횟수 미만이면 정지 대상이 아니기 때문에 삭제해 줍니다. 이 코드는 꼭 필요하지는 안습니다, 이후에 id_list에 있는 각 user별로 정지 이메일을 몇통 받을 지 계산할 때, 딕셔너리의 모든 키를 방문해야 하게 됩니다. 이 때, 딕셔너리의 키의 개수가 작으면 문제를 좀 더 빠른 속도로 풀 수 있어서, k 횟수 미만 신고된 유저는 딕셔너리에서 삭제합니다.

for key in list(dict.keys()):
   
if len(dict[key]) < k:
       
del dict[key]

 

이제 id_list에 있는 각 유저가 정지되었다는 이메일을 몇통 받을지 알아보는 코드입니다.

answer = [0 for _ in range(len(id_list))]
for idx in range(len(id_list)):
    id = id_list[idx]

   
for key in dict.keys():
       
if id in dict[key]:
            answer[idx] +=
1

우선 answer에 id_list길이 만큼 0으로 초기화를 해줍니다.

리스트 id_list만큼 for 루프를 실행하구요, 각 id 마다, 딕셔너리의 모든 키, 즉, 신고된 유저를 모두 방문합니다. 여기서 딕셔너리에 있는 신고된 유저는 모두 정지 이메일 발송 대상입니다.

따라서, 신고한 유저의 id가 딕셔너러리의 키가 가리키는 set 안에 들어 있다면, 정지 이메일이 발송됨으로 answer[idx] += 1을 해줍니다. 두개의 for 루프가 완료되면, 각 유저가 받을 정지 이메일의 개수를 구할 수 있습니다.

 

궁금한 내용은 댓글, 이메일(coding.data.pul@gmail.com)로 보내주세요.

코데풀 유튜브 구독 부탁드립니다.

https://www.youtube.com/@codapul

 

전체 코드는 아래에 있습니다.

from typing import List
from collections import defaultdict


def solution(id_list: List[str], reports: List[str], k: int):
    dict = defaultdict(
set)

   
for report in reports:
        reporter, reported = report.split(
' ')
        dict[reported].add(reporter)

   
for key in list(dict.keys()):
       
if len(dict[key]) < k:
           
del dict[key]

    answer = [
0 for _ in range(len(id_list))]
   
for idx in range(len(id_list)):
        id = id_list[idx]

       
for key in dict.keys():
           
if id in dict[key]:
                answer[idx] +=
1

   
return answer

 

2024년 4월 20일 토요일

2023 KAKAO BLIND RECRUITMENT 개인정보 수집 유효기간

문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/150370

파이썬 소스: https://bit.ly/3JpniM3

 

문제에서 주어지는 YYYY.MM.DD형태의 날짜를 숫자형태로 변환해야, today변수로 주어지는 날짜와 비교해서 파기해야 할 개인정보인지 알 수 있습니다. 연월일을 숫자로 변환하는 함수부터 코딩해보겠습니다.

def ymd2int(ymd: str) -> int:
    y, m, d = ymd.split(
'.')
    y =
int(y) * 12 * 28
   
m = int(m[0]) * 10 + int(m[1])
    m *=
28
   
d = int(d[0]) * 10 + int(d[1])
   
return y + m + d

‘.’를 기준으로 YYYY.MM.DD를 나눠서 y, m, d 변수에 저장합니다.

변수 y는 2000부터 시작하기 때문에, 스트링 y의 앞쪽에 0으로 시작하는 경우가 없어서, int()를 사용해서 숫자로 변환하고, 연도에 해당하는 일 수를 계산하기 위해서 * 12 개월 해주고, 매달 * 28일을 곱해줍니다.

변수 m은 인덱스 0번째 문자가 0이 될 수 있기 때문에, m[0]과 m[1] 따로 숫자로 변환하구요, m[0[이 ‘0’스트링인 경우 0이 됨으로, int(‘0’)한 값은 숫자 0이 됩니다. m[0]을 숫자로 변환한 값은 10의 자리 이기 때문에, 10을 곱해주고, m[1]를 숫자로 변환해서 서로 더해줍니다. 마지막으로, 매달 * 28일을 곱해줍니다.

변수 d는 m에서 인덱스 0을 고려해서 숫자로 변환했던 것처럼, 숫자로 변환해줍니다.

y + m + d 3개의 변수를 더하면, 연월일을 숫자로 변환한 값이 됩니다.

def solution(today: str, terms: List, privacies: List[str]):
    today = ymd2int(today)

오늘 날짜인 today를 연월일에서 숫자로 변환합니다.

약관을 처리할 차례입니다.

dict_terms = defaultdict(int)
for term in terms:
    yakkwan, yuhyo = term.split(
' ')
    dict_terms[yakkwan] =
int(yuhyo.split(' ')[0]) * 28

약관 종류(A, B, C …)을 키로 사용해서, 유효기간을 저장할 딕셔너리 dict_terms 변수를 만듭니다.

각 약관을 약관 종류와 유효기간으로 ‘ ‘을 사용해서 나눠줍니다.

키로 사용될 yakkwan은 딕셔너리의 키로 사용하구요, 유효기간은 다시 ‘ ‘ 기준으로 숫자달과 스트링 ‘달’로 나눕니다. 숫자달은 숫자로 변환한 뒤 * 28일을 해서 달을 일수로 변환합니다.

개인정보를 처리해보겠습니다.

for idx in range(len(privacies)):
    ymd, yakkwan = privacies[idx].split(
' ')
    privacies[idx] = [idx +
1, (ymd2int(ymd) + dict_terms[yakkwan])]

리스트 privacies의 길이만큼 for 루프가 동작합니다. ‘ ‘을 기준으로 연월일과 약관 종류로 나눠줍니다.

변수 idx는 0부터 시작하지만, 결과 값을 리턴 할 때는 1부터 시작하는 순서로 리턴 해야 하기에, +1을 해줍니다.

개인정보가 수집 일자를 숫자로 변환하고, 약관에 따른 유효기간을 더해줍니다.

문제를 풀기 위해서, 데이터를 변환 및 처리하는 과정이 끝났습니다.

답을 구하려면, 파기해야 하는 개인정보를 가려내야 합니다.

privacies = list(filter(lambda privacy: privacy[1] <= today, privacies))

개인정보 수집일자와 약관에 따른 유효기간이 지난 개인정보의 숫자는 today숫자보다 작거나 같습니다. 함수 filter와 lambda식을 사용해서, 해당 개인정보를 privacies에 저장합니다. 함수 filter의 리턴값은 리스트가 아니기 때문에, list()를 사용해서 리스트 변수로 변환합니다. 리스트 privacies에는 파기해야 할 개인정보만 남아 있습니다.

 

문제에서 아래와 같이 정렬해달라고 기술하고 있습니다.

이때 파기해야 할 개인정보의 번호를 오름차순으로 1차원 정수 배열에 담아 return 하도록 solution 함수를 완성해 주세요.

따라서, 이제 날짜는 필요가 없고, 개인정보의 번호만 필요합니다.

answer = list(map(lambda privacy: privacy[0], privacies))

함수 map()과 lambda식을 사용해서 개인정보의 번호만 남겨두고, 날짜는 삭제한 리스트를 answer에 저장합니다.

마지막으로 오름차순으로 정렬합니다.

answer.sort()

개인정보의 번호를 가지고 있는 리스트 answer를 리턴하면, 문제의 답을 구할 수 있습니다.

 

궁금한 내용은 댓글, 이메일(coding.data.pul@gmail.com)로 보내주세요.

코데풀 유튜브 구독 부탁드립니다.

https://www.youtube.com/@codapul

 

전체 코드는 아래에 있습니다.

from collections import defaultdict
from typing import List


def solution(today: str, terms: List, privacies: List[str]):
    today = ymd2int(today)
    dict_terms = defaultdict(
int)
   
for term in terms:
        yakkwan, yuhyo = term.split(
' ')
        dict_terms[yakkwan] =
int(yuhyo.split(' ')[0]) * 28

   
for idx in range(len(privacies)):
        ymd, yakkwan = privacies[idx].split(
' ')
        privacies[idx] = [idx +
1, (ymd2int(ymd) + dict_terms[yakkwan])]

    privacies =
list(filter(lambda privacy: privacy[1] <= today, privacies))
    answer =
list(map(lambda privacy: privacy[0], privacies))
    answer.sort()

   
return answer


def ymd2int(ymd: str) -> int:
    y, m, d = ymd.split(
'.')
    y =
int(y) * 12 * 28
   
m = int(m[0]) * 10 + int(m[1])
    m *=
28
   
d = int(d[0]) * 10 + int(d[1])
   
return y + m + d

 

 

2024년 4월 17일 수요일

2023 KAKAO BLIND RECRUITMENT 택배 배달과 수거하기

문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/150369

파이썬 소스: https://bit.ly/4aClonc

 

문제를 읽어본 후에, 아래 문장에 생각이 사로 잡히게 되면,

트럭 하나로 모든 배달과 수거를 마치고 물류창고까지 돌아올 수 있는 최소 이동 거리를 구하려 합니다.

‘언제 어느 집을 방문해야 최적화된 방문이 되는가?’라는 관점에서 최적화된 집을 먼저 방문할 지 알고리즘을 찾겠다고 접근하면 문제를 풀기 어렵습니다.

‘배달할 택배 또는 빈 택배 상자가 있는 집은 모두 방문해야 한다’라는 관점에서 문제를 접근해야 합니다. 그러면, ‘먼 집과 가까운 집 중에 어디를 먼저 방문하지?’로 관점이 옮겨 지구요. 그러면, ‘먼 집을 다녀오는 중에, 더 실을 수 있으면, 가까운 집을 들려서, 더 박스를 실을 수 있겠구나?’ 로 생각이 귀결되면, 올바르게 접근한 것입니다.

 이 문제는 트릭이, 모든 택배를 배달하고, 모든 빈 박스를 수거하면서 이동할 때, 최소 거리를 구하는 것입니다. 경로를 구하는 문제가 아닙니다. 같은 최소 거리를 가지는 다양한 경로가 존재할 수 있지만, 이 문제에서는 최소 거리만 구합니다.

여기서 한단계 더 나아가서, 리스트 deliveries와 리스트 pickups를 모두 방문하지 않고, 배달할 택배, 빈 택배 상자가 있는 집만 추려내서, 방문한다면, 문제를 조금 최적화해서 풀 수 있습니다.

dict_deliveries = defaultdict(int)
dict_pickups = defaultdict(
int)

for i in range(0, n):
   
if deliveries[i] > 0:
        dict_deliveries[i] = deliveries[i]
   
if pickups[i] > 0:
        dict_pickups[i] = pickups[i]

딕셔너리 2개를 사용해서, 방문할 필요가 있는 택배, 빈 택배가 있는 집만 딕셔너리에 저장합니다.

 

key_deliveries = list(dict_deliveries.keys())
key_pickups =
list(dict_pickups.keys())
key_deliveries.sort(
reverse=True)
key_pickups.sort(
reverse=True)

먼 집부터 방문해야 가까운 집을 중간에 방문할 수 있기 때문에, key값이 큰 순서로 sort(reverse=Ture)로 정렬합니다.

while len(key_deliveries) > 0 or len(key_pickups) > 0:

택배가 배달될 집이 있거나, 빈 택배를 수거할 집이 있는 동안 루프가 실행됩니다. 차에 실을 수 있는 용량 cap에 따라서, 어떤 집의 택배를 모두 배달 했거나, 빈 택배를 모두 수거하면, 해당 집은 리스트 key_deliveries 또는, key_pickups 에서 삭제합니다.

 

local_cap = cap
n_deliveries =
0
while local_cap > 0 and len(key_deliveries) > 0:
   
if local_cap >= dict_deliveries[key_deliveries[0]]:
        local_cap -= dict_deliveries[key_deliveries[
0]]
        n_deliveries =
max(n_deliveries, key_deliveries[0])
        key_deliveries.pop(
0)
   
else:
        dict_deliveries[key_deliveries[
0]] -= local_cap
        local_cap =
0
       
n_deliveries = max(n_deliveries, key_deliveries[0])

먼저 택배를 배달하는 코드입니다. 배달할 수 있는 만큼 local_cap 이 0 보다 크고, 배달할 택배가 있는 동안 while룰프가 실행됩니다.

리스트 key_deliveries[0] 에는 항상 제일 먼 집의 위치가 저장되어 있습니다. 그 집의 택배를 모두 실을 수 있으면, key_deliveries.pop(0)해서 그 집을 삭제합니다.

코드를 간단하게 n_deliveries를 구하기 위해서 max()를 사용합니다. 초기값은 0이고 제일 처음 방문한 집이 가장 먼 집이기 때문에, 항상 가장 먼 집의 위치가, n_deliveries에 저장됩니다.

특정 집에 택배를 모두 나를 수 없으면, 일부만 택배를 전달 하구요, 전달한 택배 개수 만큼 그집에서 빼주게 됩니다. 따라서, 더 이상 택배를 실을 수 없으므로, local_cap = 0이 됩니다.

 

빈 박스를 수거해 오는 코드는 택배를 배달하는 코드와 완전히 동일합니다.

local_cap = cap
n_pickups =
0
while local_cap > 0 and len(key_pickups) > 0:
   
if local_cap >= dict_pickups[key_pickups[0]]:
        local_cap -= dict_pickups[key_pickups[
0]]
        n_pickups =
max(n_pickups, key_pickups[0])
        key_pickups.pop(
0)
   
else:
        dict_pickups[key_pickups[
0]] -= local_cap
        local_cap =
0
       
n_pickups = max(n_pickups, key_pickups[0])

 

 한대의 트럭으로 최소 이동 거리를 계산하기 위한 값 n_deliveries와 n_pickups를 계산했습니다.

answer += ((max(n_deliveries, n_pickups) + 1) * 2)

위와 같이 둘에 어 먼 집 찾아서 이동거리를 계산합니다. 인덱스 0번에 위치하는 집의 거리는 1이기 때문에, +1을 합니다. 트럭은 왕복 운행임으로, * 2를 해서 answer에 더하게 됩니다.

위의 과정을 첫 while루프가 끝날 때까지 반복하면, 트럭의 최소 이동 거리를 구할 수 있습니다.

 

궁금한 내용은 댓글, 이메일(coding.data.pul@gmail.com)로 보내주세요.

코데풀 유튜브 구독 부탁드립니다.

https://www.youtube.com/@codapul

 

전체 코드는 아래에 있습니다.

from collections import defaultdict
from typing import List


def solution(cap: int, n: int, deliveries: List[int], pickups: List[int]):
    dict_deliveries = defaultdict(
int)
    dict_pickups = defaultdict(
int)

   
for i in range(0, n):
       
if deliveries[i] > 0:
            dict_deliveries[i] = deliveries[i]
       
if pickups[i] > 0:
            dict_pickups[i] = pickups[i]

    key_deliveries =
list(dict_deliveries.keys())
    key_pickups =
list(dict_pickups.keys())
    key_deliveries.sort(
reverse=True)
    key_pickups.sort(
reverse=True)

    answer =
0

   
while len(key_deliveries) > 0 or len(key_pickups) > 0:
        local_cap = cap
        n_deliveries =
0
       
while local_cap > 0 and len(key_deliveries) > 0:
           
if local_cap >= dict_deliveries[key_deliveries[0]]:
                local_cap -= dict_deliveries[key_deliveries[
0]]
                n_deliveries =
max(n_deliveries, key_deliveries[0])
                key_deliveries.pop(
0)
           
else:
                dict_deliveries[key_deliveries[
0]] -= local_cap
                local_cap =
0
               
n_deliveries = max(n_deliveries, key_deliveries[0])

        local_cap = cap
        n_pickups =
0
       
while local_cap > 0 and len(key_pickups) > 0:
           
if local_cap >= dict_pickups[key_pickups[0]]:
                local_cap -= dict_pickups[key_pickups[
0]]
                n_pickups =
max(n_pickups, key_pickups[0])
                key_pickups.pop(
0)
           
else:
                dict_pickups[key_pickups[
0]] -= local_cap
                local_cap =
0
               
n_pickups = max(n_pickups, key_pickups[0])

        answer += ((
max(n_deliveries, n_pickups) + 1) * 2)

   
return answer