넘치게 채우기

[프로그래머스] 매출 하락 최소화 본문

PS/Programmers

[프로그래머스] 매출 하락 최소화

riveroverflow 2023. 11. 13. 17:42
728x90
반응형

https://school.programmers.co.kr/learn/courses/30/lessons/72416

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

프로그래머스 - 매출 하락 최소화

문제 유형 : 다이나믹 프로그래밍 / DFS

문제 난이도 : Level 4

 

문제 설명

유통전문회사 카카오상사의 오너인 제이지는 새로운 사업 아이템을 구상하기 위해 전문경영인(CEO)인 프로도에게 회사의 경영을 부탁하였습니다.
"카카오상사"는 직원들을 여러 개의 팀 단위로 조직을 구성하고 있으며 아래 그림은 CEO를 포함하여 10명의 직원과 4개의 팀으로 구성되어 있는 회사 조직도를 보여주고 있습니다.

그림의 조직도는 다음과 같이 설명할 수 있습니다.

  1. 그림의 각 원들은 각각의 직원 1명을 표시하고 있으며, CEO를 포함하여 총 10명의 직원을 표시하고 있습니다.
  2. 원 안에 적힌 두 개의 숫자는 직원의 정보를 담고 있습니다. 왼쪽 숫자는 직원번호이며 직원을 식별할 수 있도록 1번부터 순서대로 발급되는 일련번호이며, 오른쪽 숫자는 해당 직원의 하루평균 매출액을 나타냅니다.
    위 그림에서 1번 직원은 14원을, 9번 직원은 28원의 하루평균 매출액을 기록하고 있습니다.
  3. 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명 이상의 직원을 워크숍에 참석시켜야 합니다.
  2. 워크숍 기간 동안, 회사의 매출 손실을 최소화하는 것이 중요하므로 워크숍에 참석하는 직원들의 하루평균 매출액의 합이 최소가 되어야 합니다.

위 그림의 조직도에서 회색으로 색칠된 1번, 7번, 10번 직원을 워크숍에 참석시키면 모든 팀에서 최소 한 명 이상의 직원을 참석시킨 것이 되며, 해당 직원들의 하루평균 매출액의 합은 44(14+13+17)원 입니다. 10번 직원은 C팀과 D팀 모두에 속해 있으므로, 두 팀에서 모두 참석한 것으로 인정됩니다.

 

직원들의 하루평균 매출액 값을 담은 배열 sales, 직원들의 팀장-팀원의 관계를 나타내는 2차원 배열 links가 매개변수로 주어집니다. 이때, 모든 팀에서 최소 한 명 이상 워크숍에 참석하면서, 참석하는 직원들의 하루평균 매출액의 합을 최소로 하려고 합니다. 그렇게 최소화된 매출액의 합을 구해서 return 하도록 solution 함수를 완성해 주세요.

 

 

풀이

 

dp[사원번호][참석여부]를 담는 2차원 배열 dp를 만듭니다.

최종적인 답은 dp[1][0]과 dp[1][1]중, 더 작은 값이 됩니다.

 

다음과 같은 방식으로 재귀가 이루어집니다:

현재 서브트리를 root라고 하겠습니다.

1. 만약 root가 리프노드라면, 탐색을 멈춤.

2. 각 자식노드들의 참석 여부별로 최솟값을 누적시킴.

3. 만약, root가 참석한다면:

    1) dp[root][1] = (각 자식노드들의 최솟값의 누적값) + (root의 매출)

4. 만약 root가 참석하지 않는다면, 

    1) 자식 노드들 중에서 이미 참여한 상태: dp[root][0] = 각 자식노드들의 최솟값의 누적값

    2) 자식 노드들 중에서도 아무도 참여를 하지 않은 상태(자식노드들 중에서 누군가는 참석해야함): dp[root][0] = 각 자식노드들의 최솟값의 누적값 + 자식노드 중 참석했을 때 가장 적은 리스크

 

코드

C++

#include <bits/stdc++.h>

using namespace std;

vector<vector<int>> dp;
vector<vector<int>> graph;
vector<int> saless;
int n;

void dfs(int root) {
    int sum_val = 0;
    int lowestGap = 1e9;
    vector<int> mins;
    int not_attended = 0;
    
    if(graph[root].size() == 0) return;

    for (const auto &child : graph[root]) {
        dfs(child);
        mins.push_back(min(dp[child][0], dp[child][1]));

        if (dp[child][0] < dp[child][1]) {
            not_attended++;
        }
        if(dp[child][1] > dp[child][0]) {
            lowestGap = min(lowestGap, dp[child][1] - dp[child][0]);
        }
    }

    for (const auto &m : mins) {
        sum_val += m;
    }

    // 팀장이 참석한 경우
    dp[root][1] = saless[root] + sum_val;

    // 팀장이 참석하지 않은 경우
    if (not_attended < graph[root].size()) {
        // 1. 팀원 중에 참가자가 있음
        dp[root][0] = sum_val;
    } else {
        // 2. 팀원 중에 참가자가 없음. 부하 직원들 중에서 가장 매출이 적은 직원을 참가.
        dp[root][0] = sum_val + lowestGap;
    }
    
    // cout << "root: " << root << " " << "attended: 1, sale: " << dp[root][1] << endl;
    // cout << "root: " << root << " " << "attended: 0, sale: " << dp[root][0] << endl;
}

int solution(vector<int> sales, vector<vector<int>> links) {
    n = sales.size();
    dp.resize(n + 1, vector<int>(2, 0));
    graph.resize(n + 1);
    saless = vector<int>(n + 1, 0);

    for (int i = 0; i < n; i++) {
        saless[i + 1] = sales[i];
    }

    for (const auto &link : links) {
        int a = link[0];
        int b = link[1];
        graph[a].push_back(b);
    }

    for (int i = 1; i <= n; i++) {
        // 자식 노드가 없는 노드들은 dp초기값 설정 가능
        if (graph[i].empty()) {
            dp[i][1] = saless[i];
            dp[i][0] = 0;
        }
    }

    dfs(1);

    return min(dp[1][0], dp[1][1]);
}
 
728x90
반응형