[level 4] 매출 하락 최소화 - 72416
성능 요약
메모리: 10.2 MB, 시간: 0.02 ms
구분
코딩테스트 연습 > 2021 KAKAO BLIND RECRUITMENT
채점결과
Empty
문제 설명
유통전문회사 카카오상사
의 오너인 제이지
는 새로운 사업 아이템을 구상하기 위해 전문경영인(CEO)인 프로도
에게 회사의 경영을 부탁하였습니다.
"카카오상사"는 직원들을 여러 개의 팀 단위로 조직을 구성하고 있으며 아래 그림은 CEO를 포함하여 10명의 직원과 4개의 팀으로 구성되어 있는 회사 조직도를 보여주고 있습니다.
그림의 조직도는 다음과 같이 설명할 수 있습니다.
- 그림의 각 원들은 각각의 직원 1명을 표시하고 있으며, CEO를 포함하여 총 10명의 직원을 표시하고 있습니다.
- 원 안에 적힌 두 개의 숫자는 직원의 정보를 담고 있습니다. 왼쪽 숫자는
직원번호
이며 직원을 식별할 수 있도록 1번부터 순서대로 발급되는 일련번호이며, 오른쪽 숫자는해당 직원의 하루평균 매출액
을 나타냅니다. 위 그림에서1번
직원은 14원을,9번
직원은 28원의 하루평균 매출액을 기록하고 있습니다. - CEO를 포함하여 모든 직원은 팀장 또는 팀원이라는 직위를 가지고 있으며 그림에서는 팀장과 팀원의 관계를 화살표로 표시하고 있습니다. 화살표가 시작되는 쪽의 직원은 팀장, 화살표를 받는 쪽의 직원은 팀원을 의미합니다.
3-1. 직원번호1번
은 회사의 CEO로 고정되어 있으며, CEO는 항상 팀장이고 팀원일 수 없어 화살표를 받는 쪽이 될 수 없습니다.
3-2. 반면에 CEO를 제외한 나머지 모든 직원들은 다른 누군가로부터 정확히 1개의 화살표를 받게 됩니다.
3-3. 한 직원은 최대 2개의 팀에 소속될 수 있습니다. 만약 어떤 직원이 두 개의 팀에 소속되어 있다면, 반드시 하나의 팀에서는 팀장, 나머지 팀에서는 팀원이어야 합니다. 팀장을 겸임하거나, 두 개의 팀에서 팀원이 될 수는 없습니다. 예를들어10번
직원은D팀
의 팀장이면서 동시에5번
직원이 팀장으로 있는C팀
에 속한 팀원입니다.
3-4.5번, 9번, 10번
직원은 받는 쪽의 화살표와 시작하는 화살표가 모두 있으므로 팀장인 동시에 팀원입니다.
3-5.2번, 3번, 4번, 6번, 7번, 8번
직원은 시작하는 화살표가 없고 받는 쪽의 화살표만 있으므로 팀장이 아니며 오직 팀원입니다.
3-6.1번
직원인 CEO는 받는 쪽의 화살표가 없고 시작하는 화살표만 있으며 항상 팀원이 아닌 팀장입니다.
3-7. 그림의 조직도에는A, B, C, D
총 4개의 팀이 존재하며, 각각1번, 9번, 5번, 10번
직원이 팀장 직위를 담당하게 됩니다.
"제이지"는 자신이 구상한 새로운 사업 아이템에 대해 직원들에게 설명하고자 하루 일정으로 워크숍을 계획하고 있습니다. 단, 모든 직원을 참석시킬 수 없어 아래와 같은 기준으로 워크숍에 참석할 직원들을 선발하려고 합니다.
- 워크숍에서 교육받은 내용은 전 직원들에게 공유되어야 하므로
모든 팀은 최소 1명 이상
의 직원을 워크숍에 참석시켜야 합니다. - 워크숍 기간 동안, 회사의 매출 손실을 최소화하는 것이 중요하므로 워크숍에 참석하는 직원들의 하루평균 매출액의 합이 최소가 되어야 합니다.
위 그림의 조직도에서 회색으로 색칠된 1번, 7번, 10번
직원을 워크숍에 참석시키면 모든 팀에서 최소 한 명 이상의 직원을 참석시킨 것이 되며, 해당 직원들의 하루평균 매출액의 합은 44(14+13+17)원
입니다. 10번 직원은 C팀과 D팀 모두에 속해 있으므로, 두 팀에서 모두 참석한 것으로 인정됩니다.
[문제]
직원들의 하루평균 매출액 값을 담은 배열 sales, 직원들의 팀장-팀원
의 관계를 나타내는 2차원 배열 links가 매개변수로 주어집니다. 이때, 모든 팀에서 최소 한 명 이상 워크숍에 참석하면서, 참석하는 직원들의 하루평균 매출액의 합을 최소로 하려고 합니다. 그렇게 최소화된 매출액의 합을 구해서 return 하도록 solution 함수를 완성해 주세요.
[제한사항]
- sales 배열의 크기는 2 이상 300,000 이하입니다. sales 배열의 크기는 CEO를 포함한 전체 직원 수와 같습니다.
- sales 배열은 각 직원들의 하루평균 매출액을 담고 있으며,
1번
직원부터 직원번호 순서대로 주어집니다. - sales 배열의 각 원소의 값은 0 이상 10,000 이하인 정수입니다.
- sales 배열은 각 직원들의 하루평균 매출액을 담고 있으며,
- links 배열의 크기는
sales 배열의 크기 - 1
입니다. 즉, 전체 직원 수보다 1이 작습니다. - links 배열의 각 원소는 [a, b] 형식입니다.
- a는 팀장의 직원번호, b는 a팀장이 관리하는 팀원의 직원번호이며, a와 b는 서로 다른 자연수입니다.
- 1 ≤
a
≤sales 배열의 크기
입니다. - 2 ≤
b
≤sales 배열의 크기
입니다. - 직원번호 1은 CEO로 정해져 있고 CEO는 항상 팀장으므로 b ≠ 1 입니다.
- links 배열로 만들어지는 조직도는 하나의 트리 구조 형태입니다.
- 정답으로 return 되는 값은 231 - 1 이하인 자연수임이 보장됩니다.
[입출력 예]
sales | links | result |
---|---|---|
[14, 17, 15, 18, 19, 14, 13, 16, 28, 17] | [[10, 8], [1, 9], [9, 7], [5, 4], [1, 5], [5, 10], [10, 6], [1, 3], [10, 2]] | 44 |
[5, 6, 5, 3, 4] | [[2,3], [1,4], [2,5], [1,2]] | 6 |
[5, 6, 5, 1, 4] | [[2,3], [1,4], [2,5], [1,2]] | 5 |
[10, 10, 1, 1] | [[3,2], [4,3], [1,4]] | 2 |
입출력 예에 대한 설명
입출력 예 #1
문제 예시와 같습니다.
입출력 예 #2
직원번호가 2인 직원 한 명을 워크숍에 참석시키는 것이 최선이며, 2번 직원의 하루평균 매출액은 6원입니다. 따라서 6을 return 해주어야 합니다.
입출력 예 #3
직원번호가 4, 5인 직원 두 명을 워크숍에 참석시키는 것이 최선이며, 4번, 5번 직원의 하루평균 매출액의 합은 5(1+4)원 입니다. 따라서 5를 return 해주어야 합니다.
입출력 예 #4
직원번호가 3, 4인 직원 두 명을 워크숍에 참석시키는 것이 최선이며, 3번, 4번 직원의 하루평균 매출액의 합은 2(1+1)원 입니다. 따라서 2를 return 해주어야 합니다.
초기 풀이 아이디어
1. 같은 팀이다라는 것을 알 수 있어야함 <- 팀장여부를 확인할 필요가 있음.
2. 중복처리에 대한 고민이 필요함. <- 한 사람이 참여하여 두 팀에 영향을 줄 수 있는 경우가 존재함
아래 코드는 1에 대한 해결만을 제시한 풀이임. 중복 처리를 위한 2번 항목은 처리하지 못하는 코드
indexCheck = 1
def solution(sales, links):
answer = 0
teams = dict()
for link in links:
if link[0] in teams:
teams[link[0]].append(link[1])
else:
teams[link[0]] = [link[0],link[1]]
for team in teams.values():
min = 10001
for val in team:
if min > sales[val-indexCheck]:
min = sales[val-indexCheck]
answer += min
return answer
중복까지 처리가 가능한 내 풀이
1. 현재 team의 최솟값을 구한다
2. 현재 team을 끝내기 전에 재귀로 다음 team을 호출한다.(말단까지 내려감)
3 이전 team의 최솟값과 말단 team의 최솟값의 합이 말단 team의 leader node의 값 보다 크다면 leader node가 채택된다.
라는 아이디어로 풀어봤으나 1/3은 오답, 1/3은 시간초과, 1/3은 맞추는 상태가 되었다.
로직이 오답이 있다는 것은 로직이 잘못되었다는 것이고, 다른 사람의 로직을 참고해 보기로 했다.
indexCheck = 1
answer = []
leaders = []
teams = dict()
def recursionCall(xMin, leader,sales):
leaders.remove(leader)
nextLeaders = []
min = [10001,-1]
team = teams[leader]
check = -1
for val in team:
if val in leaders:
nextLeaders.append(val)
if min[0] > sales[val-indexCheck]:
min = [sales[val-indexCheck],val]
for nextLeader in nextLeaders:
if nextLeader != -1:
check = recursionCall(min, nextLeader,sales)
if check != 1:
if (xMin[0] + min[0]) > sales[leader - 1]:
answer.append([sales[leader-1],leader])
return 1
else:
answer.append(min)
if not nextLeaders:
if check != 1:
if (xMin[0] + min[0]) > sales[leader - 1]:
answer.append([sales[leader-1],leader])
return 1
else:
answer.append(min)
def solution(sales, links):
for link in links:
if link[0] in teams:
teams[link[0]].append(link[1])
else:
leaders.append(link[0])
teams[link[0]] = [link[0],link[1]]
recursionCall([0,-1],1,sales)
total = 0
for i in range(len(answer)):
total += answer[i][0]
for j in range(i+1,len(answer)):
if answer[i] == answer[j]:
total -= answer[i][0]
break
return total
다른 사람의 풀이
참고링크 : https://daruiniji.tistory.com/20
DP로 풀이한 풀이이다.
코드를 해석해 보자면 말단 노드인 경우와 아닌 경우를 for문으로 구분해 주었고,
말단 노드가 아니라면 sum_child(이전 노드의 최솟값)을 받아온다.
그 후 2가지 경우로 dp를 구분하여 저장해 주었는데,
dp [i][0] 은 나 자신을 포함하지 않는 값이고,
dp [i][1] 은 나 자신을 반드시 포함하는 값이다. 따라서 dp [i][1] 에는 자신의 매출과 이전의 최솟값을 더한 값을 저장해 주고,
dp [i][0] 에는 이전 값이 팀장이 최소인지 아닌지를 구분하여 결정한다.
이전 값이 팀장을 포함하면서 전 팀 중에서 최솟값이라면 우리 팀에서는 아무도 차출되지 않아도 괜찮기 때문에 sum_child만을 넣어주고,
이전 값에 팀장이 포함되지 않는다면 우리 팀에서도 1명이 선택되어야 하기 때문에 sum_child 값에 우리 팀 내의 최소 값을 넣어준다.
그렇게 가장 루트인 1번 사원의 dp 값 중 최솟값이 전체 최솟값이 되는 방식이다.
def solution(sales, links):
link=[[] for _ in sales]
dp=[[0,0] for _ in sales]
for a,b in links: link[a-1].append(b-1)
def solve(i):
sum_child=0
for k in link[i]:
solve(k)
sum_child+=min(dp[k][0],dp[k][1])
dp[i][1]=sum_child + sales[i]
if any(dp[k][0]>dp[k][1] for k in link[i]):
dp[i][0]=sum_child
else:
dp[i][0]=sum_child + min(
(dp[k][1]-dp[k][0] for k in link[i]),
default=0)
solve(0)
return min(dp[0][0],dp[0][1])
'알고리즘 문제 풀이 > 프로그래머스' 카테고리의 다른 글
[프로그래머스] 폰켓몬 - python[Hash] (1) | 2023.08.29 |
---|---|
[프로그래머스]주차 요금 계산 - python[String, 구현] (1) | 2023.08.20 |
[프로그래머스] 단속 카메라 - python[Greedy] (1) | 2023.08.18 |
[프로그래머스] 타겟 넘버 - python[BFS] (0) | 2023.07.30 |
[프로그래머스] 전력망을 둘로 나누기 - python[DFS, 위상정렬] (1) | 2023.07.29 |