프로그래머스 - 다단계 칫솔 판매 (Lv.3)

3 minute read

문제 링크

문제 풀이

  • 다단계 조직의 구성원들과 각 구성원을 추천한 인물들을 매칭하는 작업이 필요하다.
  • 데이터 간의 관계를 파악하는 것이 중요하다. enroll 배열은 조직의 모든 구성원의 이름을 담고 있고, referral, seller 배열에 담긴 이름은 모두 enroll 배열에 있다. 따라서, 각 이름을 인덱스로 표현할 수 있다.
  • seller 에 담긴 수익을 낸 구성원부터 트리를 타고 올라가듯이 추천인 탐색을 하며 수익 계산을 해야하고, 추천인이 없이 들어온 사람까지 수익 배분이 끝나야 하나의 로직을 클리어하게 된다.

구현(TC 2개 시간초과)

#include <string>
#include <vector>
#include <iostream>

using namespace std;

void convert_recommander_to_idx(vector<string>& enroll, int idx, vector<int>& referral_t, string name){
    int n = enroll.size();
    for(int i=0; i<n; i++){
        if(enroll[i] == name){
            referral_t[idx] = i;
        }
    }
}

void convert_seller_to_idx(vector<string>& enroll, int idx, vector<int>& seller_t, string name){
    int n = enroll.size();
    for(int i=0; i<n; i++){
        if(enroll[i] == name){
            seller_t[idx] = i;
        }
    }
}

void update_money(vector<int>& answer, vector<int>& seller_t, vector<int>& referral_t, int profit, int idx){
    int send_money = profit / 10;
    int my_money = profit - send_money;
    
    int seller_idx = seller_t[idx]; 
    answer[seller_idx] += my_money;
    
    
    int recommander_money = send_money;
    int recommander_idx = referral_t[seller_idx];
    
    vector<int> remain_list;
    while(recommander_idx != -1){
        remain_list.push_back(recommander_idx);
        recommander_idx = referral_t[recommander_idx];
    }
    
    int remain_idx;
    for(int i=0; i<remain_list.size(); i++){
        my_money = send_money - send_money / 10;
        send_money = send_money / 10;
        remain_idx = remain_list[i];
        answer[remain_idx] += my_money;
    }
}

vector<int> solution(vector<string> enroll, vector<string> referral, vector<string> seller, vector<int> amount) {
    
    int n = enroll.size();
    vector<int> referral_t(n);
    vector<int> answer(n, 0);
    
    string rname;
    for(int i=0; i<n; i++){
        rname = referral[i];
        if(rname=="-") {
            referral_t[i]=-1; // center
            continue;
        }
        convert_recommander_to_idx(enroll, i, referral_t, rname);
    }
    
    int sell_len = seller.size();
    vector<int> seller_t(sell_len);
    string sname;
    for(int i=0; i<sell_len; i++){
        sname = seller[i];
        convert_seller_to_idx(enroll, i, seller_t, sname);
    }
    
    for(int i=0; i<sell_len; i++){
        update_money(answer, seller_t, referral_t, amount[i]*100, i);
    }
    
    return answer;
}

개선한 점

  • 본인에게 들어온 돈(실제로 판매한 돈 혹은 추천인으로부터 받은 돈)의 90%만 받고, 10%는 윗 단으로 보내는 로직을 재귀적으로 구현한다.
  • 재귀적으로 구현하면 기저사례를 적용하여 재귀호출을 제한할 수 있어서 시간 초과 테스트 케이스를 해결할 수 있다.

구현(All Pass)


#include <string>
#include <vector>
#include <iostream>

using namespace std;

void convert_recommander_to_idx(vector<string>& enroll, int idx, vector<int>& referral_t, string name){
    int n = enroll.size();
    for(int i=0; i<n; i++){
        if(enroll[i] == name){
            referral_t[idx] = i;
        }
    }
}

void convert_seller_to_idx(vector<string>& enroll, int idx, vector<int>& seller_t, string name){
    int n = enroll.size();
    for(int i=0; i<n; i++){
        if(enroll[i] == name){
            seller_t[idx] = i;
        }
    }
}

void update_money(vector<int>& answer, vector<int>& referral_t, int seller_idx, int profit){
    // 기저사례: 나한테 들어온 돈이 0일때
    if(profit==0) return;
    // 기저사례: seller_idx==-1 인 경우
    if(seller_idx==-1) return;
    
    int send_money = profit/10;
    int my_money = profit - send_money;
    answer[seller_idx] += my_money;
    
    int nseller_idx = referral_t[seller_idx];
    update_money(answer, referral_t, nseller_idx, send_money);
}

vector<int> solution(vector<string> enroll, vector<string> referral, vector<string> seller, vector<int> amount) {
    
    int n = enroll.size();
    vector<int> referral_t(n);
    vector<int> answer(n, 0);
    
    string rname;
    for(int i=0; i<n; i++){
        rname = referral[i];
        if(rname=="-") {
            referral_t[i]=-1; // center
            continue;
        }
        convert_recommander_to_idx(enroll, i, referral_t, rname);
    }
    
    int sell_len = seller.size();
    vector<int> seller_t(sell_len);
    string sname;
    for(int i=0; i<sell_len; i++){
        sname = seller[i];
        convert_seller_to_idx(enroll, i, seller_t, sname);
    }
    
    int seller_idx;
    for(int i=0; i<sell_len; i++){
        seller_idx = seller_t[i];
        update_money(answer, referral_t, seller_idx, amount[i]*100);
    }
    
    return answer;
}

구현(Updated)

#include <string>
#include <vector>
#include <map>

using namespace std;

void setProfit(map<string, string>& relation, map<string, int>& incomes, string worker, int money){
    if(worker=="-") return;
    if(money==0) return;
    
    int sendMoney = money/10;
    int myMoney = money - sendMoney;
    incomes[worker] += myMoney;
    string recommender = relation[worker];
    setProfit(relation, incomes, recommender, sendMoney);
}

vector<int> solution(vector<string> enroll, vector<string> referral, vector<string> seller, vector<int> amount) {
    vector<int> answer;
    // relation & incomes init
    map<string, string> relation;
    map<string, int> incomes; // 직원 별 수익
    for(int i=0; i<enroll.size(); i++){
        relation[enroll[i]] = referral[i];
        incomes[enroll[i]] = 0;
    }
    
    // distribute profits
    string worker; int profit;
    for(int i=0; i<seller.size(); i++){
        worker = seller[i];
        profit = amount[i] * 100;
        setProfit(relation, incomes, worker, profit);
    }
    
    for(int i=0; i<enroll.size(); i++){
        worker = enroll[i];
        answer.push_back(incomes[worker]);
    }
    
    return answer;
}

Leave a comment