문제 설명

카카오에 신입 개발자로 입사한 "콘"은 선배 개발자로부터 개발역량 강화를 위해 다른 개발자가 작성한 소스 코드를 분석하여 문제점을 발견하고 수정하라는 업무 과제를 받았습니다. 소스를 컴파일하여 로그를 보니 대부분 소스 코드 내 작성된 괄호가 개수는 맞지만 짝이 맞지 않은 형태로 작성되어 오류가 나는 것을 알게 되었습니다.
수정해야 할 소스 파일이 너무 많아서 고민하던 "콘"은 소스 코드에 작성된 모든 괄호를 뽑아서 올바른 순서대로 배치된 괄호 문자열을 알려주는 프로그램을 다음과 같이 개발하려고 합니다.

용어의 정의

'(' 와 ')' 로만 이루어진 문자열이 있을 경우, '(' 의 개수와 ')' 의 개수가 같다면 이를 균형잡힌 괄호 문자열이라고 부릅니다.
그리고 여기에 '('와 ')'의 괄호의 짝도 모두 맞을 경우에는 이를 올바른 괄호 문자열이라고 부릅니다.
예를 들어, "(()))("와 같은 문자열은 "균형잡힌 괄호 문자열" 이지만 "올바른 괄호 문자열"은 아닙니다.
반면에 "(())()"와 같은 문자열은 "균형잡힌 괄호 문자열" 이면서 동시에 "올바른 괄호 문자열" 입니다.

'(' 와 ')' 로만 이루어진 문자열 w가 "균형잡힌 괄호 문자열" 이라면 다음과 같은 과정을 통해 "올바른 괄호 문자열"로 변환할 수 있습니다.

1. 입력이 빈 문자열인 경우, 빈 문자열을 반환합니다.
2. 문자열 w를 두 "균형잡힌 괄호 문자열" u, v로 분리합니다. 단, u는 "균형잡힌 괄호 문자열"로 더 이상 분리할 수 없어야 하며, v는 빈 문자열이 될 수 있습니다.
3. 문자열 u가 "올바른 괄호 문자열" 이라면 문자열 v에 대해 1단계부터 다시 수행합니다.
    3-1. 수행한 결과 문자열을 u에 이어 붙인 후 반환합니다.
4. 문자열 u가 "올바른 괄호 문자열"이 아니라면 아래 과정을 수행합니다.
    4-1. 빈 문자열에 첫 번째 문자로 '('를 붙입니다.
    4-2. 문자열 v에 대해 1단계부터 재귀적으로 수행한 결과 문자열을 이어 붙입니다.
    4-3. ')'를 다시 붙입니다.
    4-4. u의 첫 번째와 마지막 문자를 제거하고, 나머지 문자열의 괄호 방향을 뒤집어서 뒤에 붙입니다.
    4-5. 생성된 문자열을 반환합니다.

"균형잡힌 괄호 문자열" p가 매개변수로 주어질 때, 주어진 알고리즘을 수행해 "올바른 괄호 문자열"로 변환한 결과를 return 하도록 solution 함수를 완성해 주세요.

매개변수 설명

  • p는 '(' 와 ')' 로만 이루어진 문자열이며 길이는 2 이상 1,000 이하인 짝수입니다.
  • 문자열 p를 이루는 '(' 와 ')' 의 개수는 항상 같습니다.
  • 만약 p가 이미 "올바른 괄호 문자열"이라면 그대로 return 하면 됩니다.

입출력 예

p result
"(()())()" "(()())()"
")(" "()"
"()))((()" "()(())()"

내 문제 풀이

구현의 시뮬레이션 방식으로, 문제에서 주어진 대로 구현하였다.

내 코드 - 파이썬

import copy

# u, v 변환 
def balance(p):
    count = 0
    idx = -1
    for cp in p:
        if cp == "(":
            count -= 1
        elif cp == ")":
            count += 1
        idx += 1
        
        if count == 0:
            return idx
    

# 올바른 괄호 문자열 판단
def correct(p):
    copy_p = copy.deepcopy(p)
    
    while("()" in copy_p):
        copy_p = copy_p.replace("()", "")
    if copy_p == "":
        return True
    return False


def solution(p):    
    answer = ''
    
    if p=="": return ""
    # 이미 올바른 괄호 문자열인 경우
    if correct(p): return p
    
    # u = 균형잡힌 괄호 문자열로 변환
    idx = balance(p)
    u, v = p[:idx+1], p[idx+1:]

    # u가 올바른 문자열인 경우
    if correct(u): 
        return u+solution(v)
    
    # u가 올바른 문자열이 아닌 경우
    if not correct(u):
        answer=('('+solution(v)+')')
        slice_u = u[1:-1]
        for i in slice_u:
            if i=='(':
                answer += ')'
            else:
                answer += '('
        return answer

내 코드 - C++

C++ 이용 알고리즘을 공부하며 python 코드를 C++화 하였다.

#include <string.h>
#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

int balance(string p){
    int count = 0;
    int idx = -1;
    
    for(auto cp : p){
        if(cp=='('){
            count -= 1;
        }
        else if(cp==')'){
            count += 1;
        }
        idx  += 1;
        
        if(count == 0){
            return idx;
        }
    }
}

bool correct(string p){
    string copy_p = p;
    while(1){
        if(copy_p.find("()")==std::string::npos){
            break;
        }
        for(int i = 0; i < copy_p.length()-1; i++){
            if (copy_p[i]=='(' && copy_p[i+1] ==')'){
                // erase(x,y) : x부터 y개 지우기
                copy_p.erase(i, 2);
                break;
            }
        }
    }
    
    if(copy_p==""){return true;}
    return false;
}

string solution(string p) {
    string answer = "";
    
    if(p==""){return p;}
    //이미 올바른 괄호 문자열인 경우
    if (correct(p)){return p;}
    
    // u = 균형잡힌 괄호 문자열로 변환
    int idx = balance(p);
    string u = p.substr(0, idx+1); //문자열 슬라이싱
    string v = p.substr(idx+1, p.length());
    
    if(correct(u)){
        return u+solution(v);
    }
    
    if(not correct(u)){
        answer = ('('+solution(v)+')');
        // ex. str.substr(0,a); //idx 0부터 a개
        // ex. Str.substr(index+1, strl.length()); //index+1 부터 끝까지
        string slice_u = u.substr(1, u.length()-2);
        cout << u << endl;
        for(auto su : slice_u){
            if(su=='('){
                answer += ')';
            }
            else{
                answer += '(';
            }
        }
        return answer;
    }
}

문제 설명

데이터 처리 전문가가 되고 싶은 "어피치"는 문자열을 압축하는 방법에 대해 공부를 하고 있습니다. 최근에 대량의 데이터 처리를 위한 간단한 비손실 압축 방법에 대해 공부를 하고 있는데, 문자열에서 같은 값이 연속해서 나타나는 것을 그 문자의 개수와 반복되는 값으로 표현하여 더 짧은 문자열로 줄여서 표현하는 알고리즘을 공부하고 있습니다.
간단한 예로 "aabbaccc"의 경우 "2a2ba3c"(문자가 반복되지 않아 한번만 나타난 경우 1은 생략함)와 같이 표현할 수 있는데, 이러한 방식은 반복되는 문자가 적은 경우 압축률이 낮다는 단점이 있습니다. 예를 들면, "abcabcdede"와 같은 문자열은 전혀 압축되지 않습니다. "어피치"는 이러한 단점을 해결하기 위해 문자열을 1개 이상의 단위로 잘라서 압축하여 더 짧은 문자열로 표현할 수 있는지 방법을 찾아보려고 합니다.

예를 들어, "ababcdcdababcdcd"의 경우 문자를 1개 단위로 자르면 전혀 압축되지 않지만, 2개 단위로 잘라서 압축한다면 "2ab2cd2ab2cd"로 표현할 수 있습니다. 다른 방법으로 8개 단위로 잘라서 압축한다면 "2ababcdcd"로 표현할 수 있으며, 이때가 가장 짧게 압축하여 표현할 수 있는 방법입니다.

다른 예로, "abcabcdede"와 같은 경우, 문자를 2개 단위로 잘라서 압축하면 "abcabc2de"가 되지만, 3개 단위로 자른다면 "2abcdede"가 되어 3개 단위가 가장 짧은 압축 방법이 됩니다. 이때 3개 단위로 자르고 마지막에 남는 문자열은 그대로 붙여주면 됩니다.

압축할 문자열 s가 매개변수로 주어질 때, 위에 설명한 방법으로 1개 이상 단위로 문자열을 잘라 압축하여 표현한 문자열 중 가장 짧은 것의 길이를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • s의 길이는 1 이상 1,000 이하입니다.
  • s는 알파벳 소문자로만 이루어져 있습니다.

입출력 예

s result
"aabbaccc" 7
"ababcdcdababcdcd" 9
"abcabcdede" 8
"abcabcabcabcdededededede" 14
"xababcdcdababcdcd" 17

 


내 문제 풀이

1. s의 길이가 1~1000이므로, 1부터 len(s)까지 1씩 증가시키면서 문자열을 슬라이싱 한다.

2-1. 슬라이싱 길이 만큼 s를 자르고, 앞-뒤 슬라이싱 한 그룹을 비교한다. 
   앞-뒤가 같은 경우 count + 1
   앞-뒤가 다른 경우 (계산한 count + 앞 그룹의 문자열)을 string 문자열에 저장한다.

2-2. 현재 그룹이 슬라이싱 한 s의 그룹(슬라이싱 크기보다 작아서 잘린 맨 뒷 문자열들은 제외) 중 (마지막-1) 그룹이라면 예외처리를 해준다.
   2-1과 동일한 수행을 하지만, 가장 뒷 그룹까지 string 문자열에 저장하는 로직을 추가하는 것

3. 문자열 슬라이싱을 해서 나눈 s의 가장 맨 뒷(슬라이싱 크기보다 작은) 문자열들을 string에 추가한다.

4. 문자열 길이의 최솟값을 answer에 계산한다.

내 코드 - 파이썬

반복되는 코드가 많아, 간략화할 필요가 있다. 추후 진행

def impress(string, s, slicing, group):
    string+=s[slicing*group:slicing*group+slicing]
    return string

def solution(s):
    answer = len(s)
    leng = len(s)
        
    # 1~len(s)까지 문자열 슬라이싱 
    for slicing in range(1, leng+1):
        string = ""
        # 나눠 떨어지는 갯수
        grouping = leng//slicing
        # 문자열 반복 횟수
        count = 1
        # 2번 이상 반복될 수 있는 경우
        if grouping > 1:
            for group in range(0, grouping-1):
                # 앞 그룹 == 뒷 그룹
                if s[slicing*group:slicing*group+slicing] == s[slicing*(group+1):slicing*(group+1)+slicing]:
                    count += 1
                else:
                    # 이전 그룹 string에 추가
                    if count == 1:
                        string = impress(string, s, slicing, group) 
                    # 반복 횟수 + 이전 그룹 string에 추가
                    else:
                        string=(str(count)+impress(string, s, slicing, group))
                    count = 1
                # 마지막 그룹인 경우
                if group == grouping-2:
                    # 마지막 그룹이 이전에 반복되지 않은 경우, 마지막 그룹 string에 추가
                    if count == 1:
                        string = impress(string, s, slicing, group+1) 
                    # 마지막 그룹이 이전에 반복된 경우, 반복 횟수 + 이전 그룹 string에 추가
                    else:
                        string=(str(count)+impress(string, s, slicing, group+1))
            
            # 문자열 슬라이싱 반복 후 남은 문자열 string에 추가
            string+=s[slicing*grouping::]
        # 2번 이상 반복 x인 경우 그냥 s자체를 string에 추가
        else:
            string = s
        # 최소 문자열 길이 계산
        answer = min(answer, len(string))
        
    return answer

 

내 코드 - C++

C++ 이용 알고리즘을 공부하며 python 코드를 C++화 하였다.

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

using namespace std;


string impress(string str, string s, int slicing, int group){
    str += s.substr(slicing*group, slicing);
    return str;
}

int solution(string s) {
    int answer = s.size();
    int leng = s.size();
    
    for(int slicing = 1; slicing<leng+1; slicing++){
        string str = "";
        int grouping = leng/slicing;
        int count=1;
        if(grouping > 1){
            for(int group = 0; group<grouping-1; group++){
                if(s.substr(slicing*group, slicing) == s.substr(slicing*(group+1), slicing)){
                    count += 1;
                }
                else{
                    if(count ==1){
                        str = impress(str, s, slicing, group);
                    }
                    else{
                        str = (to_string(count)+impress(str, s, slicing, group));
                        count = 1;
                    }
                }
                if(group==grouping-2){
                    if(count == 1){
                        str = impress(str, s, slicing, group+1);
                    }
                    else{
                        str = (to_string(count)+impress(str, s, slicing, group+1));
                    }
                }
            }
            str += s.substr(slicing*grouping, s.size());
        }
        else{
            str = s;
        }
        answer = min(answer, (int)str.length());
    }
    
    return answer;
}

문제 설명

슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스테이지 차이가 너무 큰 것이 문제였다.

이 문제를 어떻게 할까 고민 한 그녀는 동적으로 게임 시간을 늘려서 난이도를 조절하기로 했다. 역시 슈퍼 개발자라 대부분의 로직은 쉽게 구현했지만, 실패율을 구하는 부분에서 위기에 빠지고 말았다. 오렐리를 위해 실패율을 구하는 코드를 완성하라.

  • 실패율은 다음과 같이 정의한다.
    • 스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수

전체 스테이지의 개수 N, 게임을 이용하는 사용자가 현재 멈춰있는 스테이지의 번호가 담긴 배열 stages가 매개변수로 주어질 때, 실패율이 높은 스테이지부터 내림차순으로 스테이지의 번호가 담겨있는 배열을 return 하도록 solution 함수를 완성하라.

제한사항

  • 스테이지의 개수 N은 1 이상 500 이하의 자연수이다.
  • stages의 길이는 1 이상 200,000 이하이다.
  • stages에는 1 이상 N + 1 이하의 자연수가 담겨있다.
    • 각 자연수는 사용자가 현재 도전 중인 스테이지의 번호를 나타낸다.
    • 단, N + 1 은 마지막 스테이지(N 번째 스테이지) 까지 클리어 한 사용자를 나타낸다.
  • 만약 실패율이 같은 스테이지가 있다면 작은 번호의 스테이지가 먼저 오도록 하면 된다.
  • 스테이지에 도달한 유저가 없는 경우 해당 스테이지의 실패율은 0 으로 정의한다.

입출력 예

N stages result
5 [2, 1, 2, 6, 2, 4, 3, 3] [3,4,2,1,5]
4 [4,4,4,4,4] [4,1,2,3]

 


내 문제 풀이

1. stage마다의 사용자 수를 Counter을 사용하여 계산한다.

2. 1번부터 N번까지 stage를 돌며 해당 stage에 있는 사용자 수를 이용하여 실패율을 구한다. 만약 count에 특정 stage값이 없는 경우 실패율은 0으로 설정한다.
(실패율, stage 번호)와 같이 tuple 형태로 fail 리스트에 저장한다.

3. fail리스트의 값을 먼저 실패율을 내림차순으로 정렬하고, 동일한 실패율 stage 경우 stage 번호가 작은 것부터 오름차순으로 정렬한다.

* 처음에 count 딕셔너리를 설정한 후 stage 번호 순으로 정렬을 했더니 런타임 오류가 발생
    -> 불필요한 정렬은 많은 시간을 소요

내 코드 - 파이썬

from collections import Counter

def solution(N, stages):
    answer = []
    people = len(stages)
    fail = []
    count = dict(Counter(stages))
        
    for i in range(1, N+1):
        if i in count:
            f = count[i]/people
            people -= count[i]
        else:   f = 0
        fail.append((f, i))
        
    for f in sorted(fail, key=lambda x : (-x[0], x[1])):
        answer.append(f[1])

    return answer

문제 설명

n명의 권투선수가 권투 대회에 참여했고 각각 1번부터 n번까지 번호를 받았습니다. 권투 경기는 1대1 방식으로 진행이 되고, 만약 A 선수가 B 선수보다 실력이 좋다면 A 선수는 B 선수를 항상 이깁니다. 심판은 주어진 경기 결과를 가지고 선수들의 순위를 매기려 합니다. 하지만 몇몇 경기 결과를 분실하여 정확하게 순위를 매길 수 없습니다.

선수의 수 n, 경기 결과를 담은 2차원 배열 results가 매개변수로 주어질 때 정확하게 순위를 매길 수 있는 선수의 수를 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 선수의 수는 1명 이상 100명 이하입니다.
  • 경기 결과는 1개 이상 4,500개 이하입니다.
  • results 배열 각 행 [A, B]는 A 선수가 B 선수를 이겼다는 의미입니다.
  • 모든 경기 결과에는 모순이 없습니다.

입출력 예

n results return
5 [[4, 3], [4, 2], [3, 2], [1, 2], [2, 5]] 2

입출력 예 설명

2번 선수는 [1, 3, 4] 선수에게 패배했고 5번 선수에게 승리했기 때문에 4위입니다.
5번 선수는 4위인 2번 선수에게 패배했기 때문에 5위입니다.

 


내 문제풀이

1. 각 선수마다 이긴 경우(win), 진 경우(lose) 상대 선수를 저장한다.

2. a번 선수가 b번 선수를 이겼다면, a번 선수가 진(a번 선수를 이긴) 선수들은 b번 선수가 무조건 질(b번 선수를 무조건 이길) 수 밖에 없다. 또한 a번 선수가 c번 선수한테 졌다면, a번 선수가 이긴(a번 선수한테 진) 선수들은 c번 선수가 무조건 이길(c번 선수한테 무조건 질) 수 밖에 없다.

-> 이 부분에서 시간을 많이 썼다.
a번 선수가 b번 선수를 이겼다면, "b번 선수가 이긴 선수들을 모두 a번 선수가 무조건 이긴다"라는 순서로 아래와 같이 코드 로직을 짰다. = 런타임 에러가 발생했고, 반복문을 수행하고 있는 대상을 계속 수정했기 때문이었을거다.

for idx in range(1, n+1):
    for x in win[idx]:
        win[idx].update(win[x])
    for x in lose[idx]:
        lose[idx].update(lose[x])

 

3. 각 선수마다 win, lose 길이가 n-1이면 정확한 순위를 알 수 있기 때문에 이 경우 answer + 1을 해준다.

내 코드 - 파이썬

def solution(n, results):
    answer = 0 
    win = [set() for i in range (n+1)] # i가 이긴 경우, 상대 선수
    lose = [set() for i in range (n+1)] # i가 진 경우, 상대 선수
    
    for a, b in results:
        win[a].add(b)
        lose[b].add(a)
            
    for idx in range(1, n+1):
        for x in win[idx]:
            lose[x].update(lose[idx])
        for x in lose[idx]:
            win[x].update(win[idx])
            
    for player in range(1, n+1):
        count = len(win[player]) + len(lose[player])
        if count == n-1: 
            answer += 1
            
    return answer

문제 설명

n개의 노드가 있는 그래프가 있습니다. 각 노드는 1부터 n까지 번호가 적혀있습니다. 1번 노드에서 가장 멀리 떨어진 노드의 갯수를 구하려고 합니다. 가장 멀리 떨어진 노드란 최단경로로 이동했을 때 간선의 개수가 가장 많은 노드들을 의미합니다.

노드의 개수 n, 간선에 대한 정보가 담긴 2차원 배열 vertex가 매개변수로 주어질 때, 1번 노드로부터 가장 멀리 떨어진 노드가 몇 개인지를 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 노드의 개수 n은 2 이상 20,000 이하입니다.
  • 간선은 양방향이며 총 1개 이상 50,000개 이하의 간선이 있습니다.
  • vertex 배열 각 행 [a, b]는 a번 노드와 b번 노드 사이에 간선이 있다는 의미입니다.

입출력 예

n vertex return
6 [[3, 6], [4, 3], [3, 2], [1, 3], [1, 2], [2, 4], [5, 2]] 3

입출력 예 설명

예제의 그래프를 표현하면 아래 그림과 같고, 1번 노드에서 가장 멀리 떨어진 노드는 4,5,6번 노드입니다.

 


내 문제풀이

[위상 정렬 원리 활용]

1. 먼저 노드 1부터 노드 간 연결 정보를 확인하기 위해 edge를 오름차순 정렬한다.

2. edge를 차례대로 보며 리스트 graph에 각 노드마다 연결된 다른 노드 정보를 모두 저장한다. 이 때 양방향 그래프인 것을 유의한다.

3. queue(deque())에 현재 노드와 직접 연결되어 있는 노드들을 삽입하는 작업을 반복한다. 
   - 1번 노드가 기준이므로 먼저 queue에 1을 가장 먼저 삽입한다.

4. queue의 가장 앞 원소를 빼고 해당 원소와 연결되어 있는 모든 노드를 접근한다.
   - 만약 접근한 노드가 아직 연결된 이력이 없다면(route[ ? ] == 0) 현재 원소와 연결되어 있기 때문에 queue에 삽입하고 route값을 현재 원소의 route + 1 해준다.

5. queue가 빌 때까지 위 4번 로직을 반복하는데, route란 1번 노드로부터 몇 개의 간선을 통해 연결된 것인지를 표현하는 것이다. 하지만 route의 초기화는 모두 0으로 되어있고 route[1]==0이다.
만약 [5,1]이 edge에 있었다면, if route[g] == 0: 구문에서 route[1] 값이 증가하는 일이 발생한다. 따라서 while queue: 반복문을 시작하기 전에 route[1]을 1로 설정하고 이후 노드들의 route 값은 2부터 시작하게 되도록 하였다.
노드 1부터 "얼마나 멀리 떨어져 있는가"가 핵심이기 때문에 route의 시작(route[1])을 1로 설정해도 문제가 되지 않는다.

6. 정답을 반환하기 위해 route에서 가장 큰 값(노드 1부터 가장 멀리 떨어진 값)을 가지는 노드 갯수를 계산한다.

내 코드 - 파이썬

from collections import deque


def solution(n, edge):
    answer = 0
    route = [0]*(n+1) #노드 1부터 각 노드까지의 거리
    edge.sort() #노드 1부터 연결 정보 확인하기 위해 정렬
    queue = deque() 
    graph = [[] for i in range(n+1)] #각 노드에 연결된 노드 정보 저장
    
    for e in edge: # 양방향이므로 
        graph[e[0]].append(e[1])
        graph[e[1]].append(e[0])
        
    queue.append(1)
    route[1] = 1
    
    while queue:
        now = queue.popleft()
        for g in graph[now]:
            if route[g]==0:
                queue.append(g)
                route[g] = route[now]+1
        
    # 1번 노드로부터 가장 멀리 떨어진 노드 개수 계산
    max_edge= max(route)
    for r in route:
        if r == max_edge:
            answer+=1     
            
    return answer

문제 설명

출발지점부터 distance만큼 떨어진 곳에 도착지점이 있습니다. 그리고 그사이에는 바위들이 놓여있습니다. 바위 중 몇 개를 제거하려고 합니다.
예를 들어, 도착지점이 25만큼 떨어져 있고, 바위가 [2, 14, 11, 21, 17] 지점에 놓여있을 때 바위 2개를 제거하면 출발지점, 도착지점, 바위 간의 거리가 아래와 같습니다.

제거한 바위의 위치각 바위 사이의 거리거리의 최솟값

[21, 17] [2, 9, 3, 11] 2
[2, 21] [11, 3, 3, 8] 3
[2, 11] [14, 3, 4, 4] 3
[11, 21] [2, 12, 3, 8] 2
[2, 14] [11, 6, 4, 4] 4

위에서 구한 거리의 최솟값 중에 가장 큰 값은 4입니다.

출발지점부터 도착지점까지의 거리 distance, 바위들이 있는 위치를 담은 배열 rocks, 제거할 바위의 수 n이 매개변수로 주어질 때, 바위를 n개 제거한 뒤 각 지점 사이의 거리의 최솟값 중에 가장 큰 값을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 도착지점까지의 거리 distance는 1 이상 1,000,000,000 이하입니다.
  • 바위는 1개 이상 50,000개 이하가 있습니다.
  • n 은 1 이상 바위의 개수 이하입니다.

입출력 예

distance rocks n return
25 [2, 14, 11, 21, 17] 2 4

 


내 문제풀이

1. 바위 사이 거리를 기준으로 이진탐색을 수행한다. 가능한 바위 사이 거리는 대략 0~1000000000으로 두고 최솟값을 left, 최댓값을 right로 설정한다.

2. mid는 현재 turn에서의 최소 바위 사이 거리이다. 이 보다 작을 수는 없으며, 크거나 같을 수는 있다.

3. rocks를 하나 씩 보며 각 바위 사이의 거리와 mid를 비교한다.
mid보다 작은 경우, 뒷 바위(현재 위치)를 제거하는 것으로 판단하여 count(제거한 바위 갯수) +1 한다.
이 외의 경우, 현재 turn에서 바위 사이 거리의 최솟값(변수 dist)을 계산하고 앞 바위를 현재 위치로 이동시킨다.

4. 모든 바위를 모두 탐색 후,
count가 n보다 크다면 더 적은 바위를 제거해야 하므로, mid를 줄이기위해 right = mid-1로 설정한다.
count가 n보다 작거나 같다면 더 많은 바위를 제거해야 하므로, mid를 늘리기위해 left = mid+1로 설정한다. 추가적으로 우리는 각 turn에서의 최소 바위 거리 중 "최댓값"을 구해야 하기 때문에 mid를 늘릴 때 answer을 현재 turn의 최소 바위 거리(변수 dist)를 재설정한다.

5. 이진탐색이 종료되면 answer를 반환한다.

내 코드 - 파이썬

def solution(distance, rocks, n):
    answer = 0
    left, right = 0, distance
    rocks.sort()
    rocks.append(distance)
    
    while left <= right:
        dist = 1000000000
        mid = (left+right)//2 #현재 지정한 최소 바위 사이 거리
        count = 0 # 제거한 바위 갯수
        now = 0 #앞 바위
        
        for rock in rocks: #뒷 바위
            #현재 mid를 최소라고 가정했으므로, 더 작으면 뒷 바위 삭제
            if rock-now < mid:         
                count +=1 
            else:
                dist = min(dist, rock-now)
                now = rock
                
        if count > n:
            right = mid-1
        else:
            left = mid+1
            answer = dist #최솟값 중 최댓값이므로 바위 거리를 더 키워야하는 경우 저장
            
    return answer

문제 설명

n명이 입국심사를 위해 줄을 서서 기다리고 있습니다. 각 입국심사대에 있는 심사관마다 심사하는데 걸리는 시간은 다릅니다.

처음에 모든 심사대는 비어있습니다. 한 심사대에서는 동시에 한 명만 심사를 할 수 있습니다. 가장 앞에 서 있는 사람은 비어 있는 심사대로 가서 심사를 받을 수 있습니다. 하지만 더 빨리 끝나는 심사대가 있으면 기다렸다가 그곳으로 가서 심사를 받을 수도 있습니다.

모든 사람이 심사를 받는데 걸리는 시간을 최소로 하고 싶습니다.

입국심사를 기다리는 사람 수 n, 각 심사관이 한 명을 심사하는데 걸리는 시간이 담긴 배열 times가 매개변수로 주어질 때, 모든 사람이 심사를 받는데 걸리는 시간의 최솟값을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 입국심사를 기다리는 사람은 1명 이상 1,000,000,000명 이하입니다.
  • 각 심사관이 한 명을 심사하는데 걸리는 시간은 1분 이상 1,000,000,000분 이하입니다.
  • 심사관은 1명 이상 100,000명 이하입니다.

입출력 예

n times return
6 [7, 10] 28

 


내 문제풀이

1. 심사에 소요될 수 있는 가능한 시간(1~n*max(times)을 기준으로 이진탐색을 수행한다. 최솟값(1)은 변수 left, 최댓값(n*max(times))는 변수 right, left와 right의 중간값은 변수 mid로 설정한다.

2. mid는 현재 turn에서 가정하는, 심사에 소요되는 시간이다. 이 시간동안 각 심사관들이 총 몇 명을 심사할 수 있는지 계산한다. (변수 people에 저장)

3. 만약 people이 n보다 크다면, 심사에 소요되는 시간(mid)를 줄여야하므로, right = mid-1로 설정한다. 그리고 소요 시간의 "최솟값"을 찾아야하므로, mid를 answer에 저장해둔다. 만약 people이 n과 같다면, 현재 mid보다 더 최솟값을 찾을 수 있는 확률이 있기때문에 n보다 큰 경우와 동일한 작업을 수행한다. 

4. people이 n보다 작다면, 심사에 소요되는 시간(mid)를 늘려야하므로, left = mid+1로 설정한다.

5. 이진탐색을 수행하다 left가 right보다 크게 되면 탐색을 종료하고 answer를 반환한다.

내 코드 - 파이썬

def solution(n, times):
    answer = 0
    left, right = 1, n*max(times)
    
    while left<=right:
        mid = (left+right)//2
        people=0
        for time in times:
            people += mid//time #총 시간동안 각 심사관이 처리할 수 있는 사람 수 
            #일단 총 사람 수(n)보다 넘으면 반복문 종료
            if people > n: break
        
        if people >= n: 
            right = mid-1
            answer = mid # 최솟값을 찾아야 하므로 일단 저장
        else:
            left = mid+1
    
    return answer

문제 설명

두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다.

1. 한 번에 한 개의 알파벳만 바꿀 수 있습니다. 2. words에 있는 단어로만 변환할 수 있습니다.

예를 들어 begin이 hit, target가 cog, words가 [hot,dot,dog,lot,log,cog]라면 hit -> hot -> dot -> dog -> cog와 같이 4단계를 거쳐 변환할 수 있습니다.

두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 각 단어는 알파벳 소문자로만 이루어져 있습니다.
  • 각 단어의 길이는 3 이상 10 이하이며 모든 단어의 길이는 같습니다.
  • words에는 3개 이상 50개 이하의 단어가 있으며 중복되는 단어는 없습니다.
  • begin과 target은 같지 않습니다.
  • 변환할 수 없는 경우에는 0를 return 합니다.

입출력 예

begin target words return
hit cog [hot, dot, dog, lot, log, cog] 4
hit cog [hot, dot, dog, lot, log] 0

 


내 문제풀이

1. 변환할 수 없는 경우의 예외처리를 먼저 수행한다.

2. 기준이 될 현재 단어들을 word_list에 저장하며 words에서 word_list들과 비교했을 때 한 글자만 다른 글자들을 차례대로 찾는다. word_list는 begin 단어로 초기화하고 시작한다.

3. word_list와 한 글자만 다른 글자들을 찾아 diff_word에 삽입하고 words에서 삭제한다. 
모든 word_list와 수행한 후,
- diff_word에 target이 있다면 바로 answer(begin부터 한 글자씩 달라질 때마다 +1)를 반환한다. 
- diff_word에 target이 없다면 다음 턴에 비교할 현재 단어 리스트(word_list)를 diff_word로 치환하고 2번을 반복한다.

** 수정 필요 ** Turn3의 마지막 words에서 dog, log를 지워야 함.

내 코드-파이썬

def solution(begin, target, words):
    if target not in words:
        return 0
    
    answer = 0
    word_len = len(begin)
    word_list = [begin]
    diff_word = list()
    
    while(1):
        for wl in word_list:
            diff_word.clear()
            for word in words:
                diff = 0
                for idx in range (0, word_len):
                    if wl[idx] != word[idx]: diff += 1
                    if diff > 1: break
                if diff==1: # 1글자 차이
                    diff_word.append(word)
                    words.remove(word)
        
        answer += 1            
        if target in diff_word: return answer
        else: word_list = diff_word
            

+ Recent posts