[알고리즘] 이진탐색 / 이분탐색 (Binary Search) (Python)

728x90

보통 원하는 원소를 찾고자 할 때 처음부터 끝까지 탐색하는 선형 탐색을 주로 사용한다.

 

선형 탐색은 데이터의 크기가 너무 크지 않다면 딱히 고려할 조건이 없기 때문에 구현이 쉽기 때문이다.

 

하지만 데이터의 크기가 너무 커진다면 얘기가 달라진다.

 

그래서 나온 탐색 방법이 이진 탐색이다.(이분 탐색도 같은 말)

 

먼저 기본적인 개념은 순차적으로 정렬이 된 상태에서 특정 원소를 찾을 때까지 범위를 절반씩 줄이고 더 이상 찾을 범위가 없으면 해당 원소는 없다고 판단하는 알고리즘이다.

 

N개의 Data가 있을 때 범위를 절반씩 줄여나가므로 O(logn)의 아주 효율적인 시간 복잡도를 가지는데 여기서 중요한 점은 정렬된 상태여야 한다는 것이다.

 

왜냐하면 범위를 줄여나갈 때 찾고자 하는 원소와 현재 범위의 중간값을 비교해서 일치하면 중단, 일치하지 않으면 절반에 해당하는 범위엔 찾고자 하는 원소가 없다는 전제하에 범위를 줄여나가는 방식인데 정렬된 상태가 아니라면 이처럼 범위를 줄여나갈 수가 없다.

 

따라서 정렬된 Data를 탐색하거나 정렬하여 탐색할 수 있는 범위가 주어진다.

 

참고로 Python의 sort함수를 쓰면 O(nlogn)의 시간 복잡도를 가지므로 100만의 Input정도는 충분히 정렬 후 탐색할 수 있다.

 

그림의 예시를 따라가다 보면 쉽게 이해할 수 있을 것이다. (예시는 오름차순 리스트를 하며 내림차순은 반대로 하면 된다)

 

data=[1,2,4,5,6,8,9]
data=[1,2,4,5,6,8,9]

 

먼저 리스트의 전체 범위에서 시작해야 하므로 Left와 Right 두 개의 변수가 필요하다.

 

Left는 시작을 뜻하는 0(0번째 인덱스), Right는 끝을 뜻하는 len(data)-1로 선언하면 된다.

 

Left와 Right 변수 선언
Left와 Right 변수 선언

 

이제 아주 간단한 사전 준비는 끝났다.

 

Left와 Right의 위치가 역전된다면 더 이상 탐색할 범위가 없다는 뜻이므로 Left <= Right인 동안 반복을 계속 돌려주면 된다.

 

예시로 2와 7을 찾도록 해보겠다.

 

중앙값 비교
중앙값 비교

 

빨간색 화살표는 중앙값(변수명은 mid)을 뜻하고 찾고자 하는 원소가 mid에 있는 원소보다 작으므로 왼쪽 범위를 찾아야 한다는 뜻이 된다.

 

이때 이미 mid에 위치한 원소는 탐색했으므로 Right는 mid-1로 변경해서 조금이라도 탐색 횟수를 줄여준다.

 

범위 줄인 뒤 중앙값 비교
범위 줄인 뒤 중앙값 비교

 

Right=mid-1로 범위를 바꾼 뒤 다시 중앙값(mid)을 비교해보면 2를 찾을 수 있다.

 

해당 위치의 인덱스를 저장한 뒤 반복문을 빠져나오면 된다.

 

다음은 리스트에 없는 7을 찾아보겠다.

 

중앙값 비교
중앙값 비교

 

찾고자 하는 원소가 중앙값보다 크므로 오른쪽 범위에 있단 의미고 마찬가지로 중앙값은 탐색했으므로 탐색 횟수를 줄이기 위해 Left는 mid+1로 변경한다.

 

범위 줄인 뒤 중앙값 비교
범위 줄인 뒤 중앙값 비교

 

오른쪽 범위로 이동해서 다시 중앙값을 비교해보니 찾고자 하는 원소보다 중앙값이 더 크다.

 

이 말은 왼쪽 범위에 있단 뜻이므로 Right를 mid-1로 변경한다.

 

범위 줄인 뒤 중앙값 비교
범위 줄인 뒤 중앙값 비교

 

다시 왼쪽 범위로 이동해서 중앙값을 비교해보니 찾고자 하는 원소가 중앙값보다 더 크다.

 

이 말은 중앙값보다 오른쪽 범위에 있단 뜻이므로 Left는 mid+1로 변경한다.

 

이때 Left는 Right보다 커져서 역전되므로 결국 원소를 찾지 못한 채 반복문을 빠져나오게 된다.

 

이 알고리즘이 이진 탐색이며 7개의 Data에서 2를 찾는데 2번 연산, 7이 있는지 확인하는데 3번 연산밖에 하지 않았다.

 

만약 선형 탐색이었다면 최악의 경우 7번(Data의 크기만큼)의 연산을 할 것이고 확실히 이진 탐색을 통해 연산 횟수가 줄어든 것을 알 수 있다.

 

지금은 Data의 크기가 작기 때문에 몇 번 차이가 없지만 Data의 크기가 막대하게 커지면 상당히 효율적인 알고리즘이란 사실을 체감할 수 있을 것이다.

 

다음은 위의 설명대로 구현한 전체 코드이다.

 

data=[1,2,4,5,6,8,9]

def binary(n):
    index=-1
    left=0
    right=len(data)-1
    
    print("이진 탐색 과정 :",end=" ")

    while left<=right:
        mid=(left+right)//2
        print(data[mid],end=" ")
        if data[mid]==n:
            index=mid
            break
        elif n<data[mid]:
            right=mid-1
        else:
            left=mid+1
    print()
    return index

first=binary(2)

if first==-1:
    print("없는 원소입니다.")
else:
    print(first,"번째 인덱스에 있습니다.")

second=binary(7)

if second==-1:
    print("없는 원소입니다.")
else:
    print(second,"번째 인덱스에 있습니다.")

 

초기 Index는 -1로 지정하고 찾는 원소가 있다면 해당 원소의 인덱스로 갱신한다.

 

즉 여전히 Index가 -1이라면 찾는 원소가 없단 뜻이다.

 

결과 화면
결과 화면

 

그림을 참고하여 코드를 한 줄씩 따라가다 보면 쉽게 이해할 수 있을 것이다.

 

혹시나 해서 알고리즘 구현 자체를 접한 지 얼마 안 된 사람들께 덧붙이자면 이번 코드에서는 예시를 위해 인덱스를 반환하도록 구현했지만 당연하게도 각자 접하는 문제에 맞게끔 변형하여 구현해야 된다.

 

끝으로 이진탐색을 연습할 수 있는 문제를 몇가지 추천한다.

 

https://www.acmicpc.net/problem/1920

 

1920번: 수 찾기

첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들

www.acmicpc.net

 

https://www.acmicpc.net/problem/1654

 

1654번: 랜선 자르기

첫째 줄에는 오영식이 이미 가지고 있는 랜선의 개수 K, 그리고 필요한 랜선의 개수 N이 입력된다. K는 1이상 10,000이하의 정수이고, N은 1이상 1,000,000이하의 정수이다. 그리고 항상 K ≦ N 이다. 그

www.acmicpc.net

728x90

댓글()

[코딩 테스트] 2021 엔픽셀 NPIXEL 신입공채 코딩 테스트 후기 (불합격)

취업 과정|2021. 12. 1. 19:37
728x90

엔픽셀 공고 이미지
엔픽셀 공고 이미지

 

지난 2021.11.19(금)에 엔픽셀(NPIXEL) 코딩 테스트를 봤다.

 

엔픽셀(NPIXEL)은 모바일 게임 그랑사가를 출시한 회사이며 최단기간 유니콘 기업에 등극한 기업이다.

 

이때 유니콘 기업이란 기업 가치 10억 달러 이상, 창업한 지 10년 이하인 비상장 스타트업을 뜻하며 흔히 아는 크래프톤, 비바리퍼블리카(토스), 야놀자, 위메프, 쏘카, 무신사 등이 유니콘 기업에 속한다.

 

공채 사이트에서 Next Pixels 1기란 문구를 봐선 아무래도 첫 공채인 것 같다.

 

조금 신기했던 게 메일이나 문자 내용에서 이모티콘이 상당히 많은 것으로 보아 단순 업무로 딱딱한 내용을 전송하는 게 아니라 지원자들에게 친근하게 다가가고 싶어 하는 것처럼 느껴졌다.

 

또한 코테가 끝나면 1만원 상당의 스타벅스 기프티콘도 보내주더라.(프로그래밍 직군은 코테고 다른 직군은 과제 전형 끝나면 주는 듯??)

 

각설하고 프로그래밍 직군은 과제 전형 기간에 포트폴리오로 제출 가능한 Github 주소나 링크가 있으면 제출 가능했다.

 

이때 링크는 메모장에 작성해서 Zip 파일로 압축하여 올리는 거라 난 Github 주소와 포트폴리오 사이트 주소를 같이 제출했다.

 

그리고 코테는 다소 특이한 방식으로 진행됐다.

 

11.19(금) 오전 10시 ~ 11.21(일) 오후 10시 *총 3일간 아무 때나 편할 때 접속해서 응시하면 되고 총 4문제, 3시간 동안 보면 됐다.

 

이렇게 특정 기간 동안 자유롭게 코테를 볼 수 있는 것은 삼성 SDS에서 주관한 2021 하계 대학생 알고리즘 특강을 위한 코테를 제외하곤 처음이다.

 

플랫폼은 프로그래머스에서 진행됐고 1~2번은 반드시 C++로만, 3~4번은 C++ 또는 Python을 선택해서 풀 수 있었다.

 

문제별로 언어가 제한되는 것도 특이한 경험이긴 했다.

 

게임회사다 보니까 언어를 C++과 Python으로만 제한해서 보는 것 같은데 다행히 난 C를 할 줄 알아서 비교적 어렵진 않게 C++문법을 한 번 훑고 필요할만한 라이브러리도 정리하고 첫날 바로 코테를 봤다.

 

젤 중요한 코테의 난이도는 여러 알고리즘을 접해봤다면 어렵진 않게 풀 수 있는 수준이다.

 

알고리즘을 분류하자면 구현, 그래프, DP 정도가 나왔는데 어렵게 꼬아놓은 형식이 아니라 해당 알고리즘들을 접해봤다면 시간도 넉넉해서 충분히 풀 수 있는 문제였다.

 

하지만 난 결국 한 문제를 풀지 못했다.....ㅠㅠ

DP문제였는데 점화식을 세우는 과정에서 머리가 안 돌아가더라.....

어차피 공채가 있길래 넣어본 거라 머리가 안 돌아가서 그냥 3문제 푼 거에 만족하고 껐다.

 

근데 쉬었다가 한 번 다시 세워보니 금방 나왔다.

 

결과


결과가 나왔는데 불합격 메일이 왔다. (2021.12.02)

 

결과 메일
결과 메일

 

공채 단톡방을 확인해보니 3문제 풀고 합격한 사람도 있고 4문제 풀고 떨어진 사람도 있다고 한다.

 

여태 경험한 코테들은 상위 점수별로 합격을 시키고 동점자가 많은 커트라인은 자소서를 기반으로 합, 불을 정했는데 이번 코테는 어떤 기준인지 감이 잡히지 않았다.

 

여러 가지 가능성을 추측해보자면

 

  1. 시간 복잡도의 제한으로 인해 효율적인 알고리즘만 통과했다.
  2. 기존 테스트 케이스로 조건을 거르지 못하는 예상치 못한 히든 테스트 케이스가 존재했다.
  3. 제출한 포트폴리오가 영향을 끼친다.
  4. 결과뿐만 아니라 작성한 코드에도 평가요소가 있다.

이 정도가 떠오른다.

 

1. 분명 Input과 알고리즘의 시간 복잡도를 계산하면서 구현했지만 아무래도 사람인지라 충분히 실수할 가능성은 있다.

 

2. 히든 테케도 같은 원리이다.

 

3. Github주소와 포트폴리오 사이트를 제출했고 해당 사항은 선택이었지만 점수에 들어갈 가능성을 배제할 순 없다.

 

4. 이는 라인(Line)에서 진행한 코테 방식인데 평가요소에 들어간다고 명시해야 합리적인 방식이다. 따라서 이 부분은 비교적 가능성이 낮아 보인다.

 

어찌 됐든 내 실력이 부족하여 탈락한 것이므로 어디서든 합격할 수 있게 철저히 준비해야겠다.

728x90

댓글()

[BOJ] Python 백준 1072번 게임 실버 3

728x90

https://www.acmicpc.net/problem/1072

 

1072번: 게임

김형택은 지금 몰래 Spider Solitaire(스파이더 카드놀이)를 하고 있다. 형택이는 이 게임을 이길 때도 있었지만, 질 때도 있었다. 누군가의 시선이 느껴진 형택이는 게임을 중단하고 코딩을 하기 시

www.acmicpc.net


전형적으로 이진 탐색을 사용하는 문제이다.

혹시 이진 탐색을 처음 접하거나 익숙하지 않다면 관련 포스팅을 보고 이해한 뒤 문제를 푸는게 좋을 것 같다.

 

2021.12.02 - [알고리즘/알고리즘 강의] - (알고리즘) 이진탐색 / 이분탐색 (Binary Search)

 

(알고리즘) 이진탐색 / 이분탐색 (Binary Search)

보통 원하는 원소를 찾고자 할 때 처음부터 끝까지 탐색하는 선형 탐색을 주로 사용한다. 선형 탐색은 데이터의 크기가 너무 크지 않다면 딱히 고려할 조건이 없기 때문에 구현이 쉽기 때문이다

khsung0.tistory.com

 

문제 풀이


문제에서 전체 게임 횟수 : X, 이긴 게임 횟수 : Y, 승률 : Z이다.

주의해야 할 조건은 3가지다.

 

  1. 승률을 뜻하는 Z는 소수점을 버린다.(반올림이 아니다)
  2. Z가 절대 변하지 않으면 -1을 출력한다.
  3. X는 최대 1,000,000,000 (10억)까지 가능하다.

Z에 대한 식을 나타내 보면 Z=(Y*100)/X이고 a만큼 수행했을 때 Z+1=((Y+a)*100)/(X+a)가 성립하는 최소 a를 구하는 문제이다. (승률 = 이긴 횟수/전체 횟수)

참고로 Python의 계산 방식 때문인지 처음에 Z=(Y//X)*100로 작성했더니 틀렸었다.

꼭 Z=(Y*100)//X로 작성해야하고 이 부분에 대해서 정확히 아시는 분이 계시다면 피드백 해주시면 감사합니다!!

이때 Z가 99 이상이라면 승률은 절대 변하지 않으므로 -1을 출력하고 98일 때 생각하면 X번만큼 추가할 때 99로 변하기 때문에 최대 X번까지만 반복하면 된다는 것을 알 수 있다.

이때 X는 최대 10억까지 가능하므로 순차적으로 탐색하는 O(n)의 시간 복잡도를 적용한다면 반드시 시간 초과가 날 수밖에 없다.

즉 1부터 X까지 오름차순인 상태에서 탐색 가능한 이분 탐색(이진 탐색)을 적용하면 풀 수 있을 것이다.

다음은 전체 코드다.

 

x,y=map(int,input().split()) 
z=(100*y)//x 
left=0 
right=x 
res=x 
if z>=99: 
    print(-1)
else: 
    while left<=right: 
        mid=(left+right)//2 
        if (100*(y+mid))//(x+mid)>z: 
            res=mid 
            right=mid-1 
        else: 
            left=mid+1
    print(res)


res는 정답인 최소 게임 수를 의미하는 변수로 최대 X번을 저장해놓고 더 작은 수가 있으면 갱신하는 방식으로 구현하였다.

승률이 99 이상이면 바로 -1을 출력하고 98 이하라면 이진 탐색을 실행한다.

초기 left는 0, right는 X로 설정하고 left와 right가 역전되지 않을 때까지 반복한다.

이때 left와 right 중간값을 기준으로 승률(Z)이 변하면 res를 갱신한 뒤 right는 중간값-1로, 변하지 않으면 left를 중간값+1로 변경함으로써 탐색해야 되는 범위를 절반씩 줄여준다.

즉 탐색 범위가 N이라면 범위를 절반씩 줄이므로 O(logn)의 시간 복잡도를 갖고, 10억의 Input이 들어와도 충분히 연산 가능해진다.

 

결과 화면
결과 화면


※조건을 잘 보고 Input의 크기에 따라 적절한 알고리즘을 적용하는 것이 중요한 것 같다.

728x90

댓글()

[BOJ] Python 백준 23740번 버스 노선 개편하기 골드 5

728x90

https://www.acmicpc.net/problem/23740

 

23740번: 버스 노선 개편하기

서강 나라에서는 일직선 도로를 따라 $N$개의 버스 노선을 운영 중이다. 필요할 때마다 노선을 새로 만든 탓에 겹치거나 중복되는 노선이 많다. 복잡한 버스 노선에 지친 시민들을 위해 버스 노

www.acmicpc.net

문제 풀이


눈여겨봐야 할 점은 3가지다.

 

  1. 버스 노선의 개수인 N은 20만 이하이다.
  2. 한 점 이상에서 겹쳐지면 연장 가능한 노선이다.
  3. 개편된 노선의 요금은 개편할 때 사용된 노선의 최소 요금을 적용한다.

 

문제와 그림을 보고 바로 떠오른 생각은 노선의 정보를 오름차순으로 정렬한 뒤 개편된 노선과 겹친다면 포함하는 노선, 겹치는 부분이 없다면 새로운 개편된 노선으로 판단하는 방식이다.

 

이 방식대로 연산하면 Sort를 이용하여 정렬할 때 O(nlogn), 오름차순 된 노선 정보를 처음부터 훑을 때 O(n)으로 총 O(nlogn)의 시간 복잡도가 걸린다고 생각했다.

 

따라서 소스 코드는 다음과 같다.

 

n=int(input())
bus=[]
res=[]
for i in range(n):
    bus.append(list(map(int,input().split())))
bus.sort(key=lambda x:(x[0],x[1]))
check=True
for i in bus:
    if check:
        res.append(i)
        check=False
    else:
        if i[0]<=res[len(res)-1][1]:
            if i[1]>res[len(res)-1][1]:
                res[len(res)-1][1]=i[1]
            if res[len(res)-1][2]>i[2]:
                res[len(res)-1][2]=i[2]
        else:
            res.append(i)
print(len(res))
for i in res:
    print(i[0],i[1],i[2])

 

bus는 기존 노선 정보를 저장하고 res는 개편된 노선 정보를 저장할 리스트이다.

 

6번째 줄에서 lambda를 키로 이용하여 0번째 원소, 1번째 원소를 기준으로 오름차순 정렬한다는 뜻이다.

 

check 변수를 통해 첫 노선을 개편된 노선으로 잡았고 겹치는 부분이 있을 경우로 나눠 14번째 if문은 현재 노선 정보가 개편된 노선보다 길어서 연장해야 될 때, 16번째 if문은 최소 요금으로 갱신하는 문장이다.

 

또한 겹치는 부분이 없으면 기존 노선을 개편된 노선으로 추가한다.

 

한 줄씩 원리를 따라가 보면 쉽게 이해할 수 있을 것이다.

 

결과 화면
결과 화면

 

Python으로 제출하면 시간 초과가 뜨길래 PyPy로 제출했더니 통과했다.

 

PyPy는 한 줄씩 해석하는 인터프리터 방식인 Python의 느린 동작에서 컴파일 방식을 개선하여 나온 언어로 상당히 빠른 수행 속도가 특징이다.

 

따라서 동일한 문법이기에 애초에 시간 복잡도를 비효율적으로 작성한 경우가 아니라면 Python으론 시간 초과 뜨는 코드가 PyPy로 통과하는 경우가 많다.

 

즉 올바르게 작성한 것 같은데 시간 초과가 뜬다면 PyPy로 제출하길 추천한다.

728x90

댓글()

[BOJ] Python 백준 23738번 Ресторан 브론즈 2

728x90

https://www.acmicpc.net/contest/problem/725/1

 

A번: Ресторан

최대 $100$글자의 단어가 주어진다. 모든 글자는 영어 대문자 A, B, E, K, M, H, O, P, C, T, Y, X 중 하나로 이루어져 있다. 입력이 러시아어 대문자로 주어지지 않음에 주의하자.

www.acmicpc.net

문제 풀이


눈여겨봐야 할 조건은 3가지다.

 

  1. 보이는 대로 읽어야 할 알파벳과 다르게 읽어야 할 알파벳이 구분되어있다.
  2. 최대 100글자의 단어가 주어진다.
  3. 영어 소문자로 나타내 출력해야 한다.

다르게 읽어야 할 알파벳은 다음과 같다.

B->v, E->ye, H->n, P->r, C->s, Y->u, X->h

해당 부분은 파이썬의 딕셔너리(Dictionary)를 사용하면 쉽게 치환 가능하다.

 

최대 100글자이므로 시간 복잡도는 고려할 필요가 없을 정도로 Input Size가 작다.

 

입력이 대문자로 들어오고 출력은 소문자로 해야 하므로 보이는 대로 읽어야 할 알파벳은 소문자로 치환시켜야 된다.

 

다음은 전체 코드이다.

 

word=input()
russ={"B":"v","E":"ye","H":"n","P":"r","C":"s","Y":"u","X":"h"}
res=""
for i in word:
    if i in russ:
        res+=russ[i]
    else:
        res+=i.lower()
print(res)

 

먼저 russ란 딕셔너리에 치환해야 될 알파벳들을 넣어 놓고 Input 문자열을 하나씩 반복한다.

 

만약 딕셔너리에 있는 문자라면 해당 값을 res에 이어 붙이고 없는 문자라면 lower() 함수를 이용하여 대문자를 소문자로 치환한 다음 이어 붙인다.

 

완성된 문자열을 출력하면 정답이다.

 

결과 화면
결과 화면

 

파이썬의 딕셔너리를 사용해야겠다는 생각만 떠올리면 복잡한 인덱싱 과정도 필요 없기에 쉽게 풀 수 있는 문제였다.

728x90

댓글()

[PDF 전자책] 쉽게 풀어쓴 자료구조와 알고리즘 기본 가이드

728x90

썸네일
Thumbnail

개발자로 취업하기 위해선 중요한 요소들이 몇 가지 있습니다.

 

보통 IT 직무의 채용 절차는 다음과 같습니다.

코딩 테스트 -> 필기 테스트 -> 역량 면접 -> 인성 면접 -> 건강검진 -> 입사

 

각각

  1. 알고리즘 구현 능력
  2. CS(Computer Science) 지식 (전공 이론 지식)
  3. 실무 적응 가능성
  4. 회사 및 직무 적합도
  5. 건강 이상

위의 요소가 중요합니다.

 

이 중 가장 처음으로 접하는 테스트인 알고리즘 구현 능력은 주어진 문제를 해결하는 과정을 평가하므로 적절한 상황에 적합한 알고리즘을 적용하기 위해 자료구조와 알고리즘 학습이 필수입니다.

 

따라서 이 책은 취업 준비를 위해 자료구조와 알고리즘을 복습, 독학하는 과정에서 프로그래밍 언어에 대한 자료는 많이 있지만 자료구조와 알고리즘을 같이 묶은 집약된 자료를 찾기 어려웠고 국비지원 강의를 들을 때 비전공자 분들과 협업한 경험을 통해 프로그래밍 자체에 어려움을 겪는다는 것을 알게 되어 도움이 되고자 작성하게 되었습니다.

 

이 부분에 착안하여 고등학교 수리 과외 경험을 살려 언어만 알고 있다면 비전공자도 최대한 쉽게 이해할 수 있도록 작성한 PDF 형태의 전자책입니다.

 

언어는 가장 기본으로 여겨지는 C와 이해가 쉬운 Python을 기반으로 작성하였으며 목차에서 목록을 클릭하면 해당 본문 페이지로, 본문 페이지의 제목을 클릭하면 다시 목차로 넘어올 수 있도록 만들었기에 전자책의 편의성을 극대화하였습니다.

 

공부가 주목적이라 마케팅이 전혀 없었기에 몇 권 팔리진 않았지만 지인을 포함한 구매하신 분들 전부 좋은 자료라는 호평이 이어졌습니다.

 

관심 있으신 분들은 한 번 구경하셔도 좋을 것 같습니다!!

 

https://kmong.com/gig/290286

 

누구나 쉽게 독학 가능한 자료구조 알고리즘 전자책을 드립니다. | 20000원부터 시작 가능한 총 평

1개 총 작업 개수 완료한 총 평점 5점인 코딩독학의 투잡∙노하우, 직무스킬 전자책 서비스를 1개의 리뷰와 함께 확인해 보세요. 투잡∙노하우, 직무스킬 전자책 제공 등 20000원부터 시작 가능한

kmong.com

728x90

댓글()