728x90

문제 설명

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

용어의 정의

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

'(' 와 ')' 로만 이루어진 문자열 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 하면 됩니다.

입출력 예

presult

"(()())()" "(()())()"
")(" "()"
"()))((()" "()(())()"

입출력 예에 대한 설명

입출력 예 #1
이미 "올바른 괄호 문자열" 입니다.

입출력 예 #2

  • 두 문자열 u, v로 분리합니다.
    • u = ")("
    • v = ""
  • u가 "올바른 괄호 문자열"이 아니므로 다음과 같이 새로운 문자열을 만듭니다.
    • v에 대해 1단계부터 재귀적으로 수행하면 빈 문자열이 반환됩니다.
    • u의 앞뒤 문자를 제거하고, 나머지 문자의 괄호 방향을 뒤집으면 ""이 됩니다.
    • 따라서 생성되는 문자열은 "(" + "" + ")" + ""이며, 최종적으로 "()"로 변환됩니다.

입출력 예 #3

  • 두 문자열 u, v로 분리합니다.
    • u = "()"
    • v = "))((()"
  • 문자열 u가 "올바른 괄호 문자열"이므로 그대로 두고, v에 대해 재귀적으로 수행합니다.
  • 다시 두 문자열 u, v로 분리합니다.
    • u = "))(("
    • v = "()"
  • u가 "올바른 괄호 문자열"이 아니므로 다음과 같이 새로운 문자열을 만듭니다.
    • v에 대해 1단계부터 재귀적으로 수행하면 "()"이 반환됩니다.
    • u의 앞뒤 문자를 제거하고, 나머지 문자의 괄호 방향을 뒤집으면 "()"이 됩니다.
    • 따라서 생성되는 문자열은 "(" + "()" + ")" + "()"이며, 최종적으로 "(())()"를 반환합니다.
  • 처음에 그대로 둔 문자열에 반환된 문자열을 이어 붙이면 "()" + "(())()" = "()(())()"가 됩니다.

 

전략

문제접근: 이 문제는 상당히 설명글이 길고, 이해하기가 어렵습니다. 따라서 예시에 나와있는 설명을 이해하기보다는 중간에 나와있는 1~4-5 번까지 나와있는 설명글을 읽고 그대로 알고리즘을 구현하는 것이 좋습니다.

 

 

솔루션

def check_correct(u) :
    stack = []
    dic={')':'('}
    i=0
    while i<len(u):
        if len(stack) ==0 :
            stack.append(u[i])
        elif dic.get(u[i],0) == stack[-1]:
            stack.pop()
        else:
            stack.append(u[i])
        i+=1
    if len(stack) ==0:
        return True
    else:
        return False

def reverse(u):
    u=u[1:-1]
    u2=''
    dic={'(':')',')':'('}
    for i in u:
        u2+=(dic[i])
    return u2
    
def solution(p):
    if len(p)==0:
        return p
    answer,i = '',1
    
    # 균형잡힌 괄호 문자열
    while p[:i].count('(') != p[:i].count(')'):
        i+=1
    u,v = p[:i],p[i:]
    # 올바른 괄호 문자열
    if check_correct(u):
        return u+solution(v)
    elif not check_correct(u):
        return '('+solution(v)+')'+ reverse(u)
728x90
728x90

문제 설명

△△ 게임대회가 개최되었습니다. 이 대회는 N명이 참가하고, 토너먼트 형식으로 진행됩니다. N명의 참가자는 각각 1부터 N번을 차례대로 배정받습니다. 그리고, 1번↔2번, 3번↔4번, ... , N-1번↔N번의 참가자끼리 게임을 진행합니다. 각 게임에서 이긴 사람은 다음 라운드에 진출할 수 있습니다. 이때, 다음 라운드에 진출할 참가자의 번호는 다시 1번부터 N/2번을 차례대로 배정받습니다. 만약 1번↔2번 끼리 겨루는 게임에서 2번이 승리했다면 다음 라운드에서 1번을 부여받고, 3번↔4번에서 겨루는 게임에서 3번이 승리했다면 다음 라운드에서 2번을 부여받게 됩니다. 게임은 최종 한 명이 남을 때까지 진행됩니다.

이때, 처음 라운드에서 A번을 가진 참가자는 경쟁자로 생각하는 B번 참가자와 몇 번째 라운드에서 만나는지 궁금해졌습니다. 게임 참가자 수 N, 참가자 번호 A, 경쟁자 번호 B가 함수 solution의 매개변수로 주어질 때, 처음 라운드에서 A번을 가진 참가자는 경쟁자로 생각하는 B번 참가자와 몇 번째 라운드에서 만나는지 return 하는 solution 함수를 완성해 주세요. 단, A번 참가자와 B번 참가자는 서로 붙게 되기 전까지 항상 이긴다고 가정합니다.

제한사항

  • N : 21 이상 220 이하인 자연수 (2의 지수 승으로 주어지므로 부전승은 발생하지 않습니다.)
  • A, B : N 이하인 자연수 (단, A ≠ B 입니다.)

입출력 예

 N                                    A                                     B                                     answer

8 4 7 3

입출력 예 설명

입출력 예 #1
첫 번째 라운드에서 4번 참가자는 3번 참가자와 붙게 되고, 7번 참가자는 8번 참가자와 붙게 됩니다. 항상 이긴다고 가정했으므로 4번 참가자는 다음 라운드에서 2번이 되고, 7번 참가자는 4번이 됩니다. 두 번째 라운드에서 2번은 1번과 붙게 되고, 4번은 3번과 붙게 됩니다. 항상 이긴다고 가정했으므로 2번은 다음 라운드에서 1번이 되고, 4번은 2번이 됩니다. 세 번째 라운드에서 1번과 2번으로 두 참가자가 붙게 되므로 3을 return 하면 됩니다.

 

 

전략

-문제접근: 2의 지수승이 바로 핵심입니다. 예를 들어 7과 8이 경기하려면 1번만에 바로 만나지만 8과 9가 경기하려면 4번을 경기해야합니다. 

 

-풀이: a와 b가 동시에 어떤 2의 지수승 안에 포함되어야 a와 b가 경기를 치를 수 있게 됩니다. 포함되어 있지 않다면 경기를 치를 수 없습니다.

 

 

솔루션

def solution(n,a,b):
    answer = 0

    for count in range(1,n):
        for point in range(0,2**n+1,2**count):
            if a <= point and b <= point:
                return count
            if a <= point or b <= point:
                break
728x90
728x90

문제 설명

124 나라가 있습니다. 124 나라에서는 10진법이 아닌 다음과 같은 자신들만의 규칙으로 수를 표현합니다.

  1. 124 나라에는 자연수만 존재합니다.
  2. 124 나라에는 모든 수를 표현할 때 1, 2, 4만 사용합니다.

예를 들어서 124 나라에서 사용하는 숫자는 다음과 같이 변환됩니다.

10진법124 나라10진법124 나라

1 1 6 14
2 2 7 21
3 4 8 22
4 11 9 24
5 12 10 41

자연수 n이 매개변수로 주어질 때, n을 124 나라에서 사용하는 숫자로 바꾼 값을 return 하도록 solution 함수를 완성해 주세요.

제한사항

  • n은 500,000,000이하의 자연수 입니다.

입출력 예

 n                                                                 result

1 1
2 2
3 4
4 11

 

전략

-문제 접근

124 나라는 3진법과 유사하게 표현합니다. 하지만 3진법과 표현 방법이 완전히 다릅니다. 예를 들어, 12의 경우 3진법의 표현은 다음과 같습니다. 9+3 = 110(3) 으로, 3자리수로 표현됩니다. 하지만 124 나라의 경우 44(124)로 표현이 됩니다. 두 개의 표현 방법 모두 3과 관련이 되어 있다는 것은 명백하다는 것을 알 수 있습니다.

 

-풀이

124 나라의 경우 자릿수가 1자리일 때 표현가짓수는 3가지입니다. ex) 1, 2, 4

자릿수가 2자리일 때는 표현가짓수가 9가지입니다. ex) 11, 12, 14, 21, 22, 24, 41, 42, 44

즉, 자릿수가 n자리일 때 표현가지수는 3^n 개가 됩니다.

이로부터 알 수 있는 것은 해당하는 숫자의 자릿수가 몇자리나 되는지입니다. 예를 들면 4의 경우 4 - 3^1 - 3^0>= 0 이므로 4는 2자리 수라는 것을 알 수 있습니다. 13의 경우 13 - 3^2- 3^1 - 3^0 >= 0 이므로 13은 3자리 수라는 것을 알 수 있습니다. 

 

한편, 각 자리에 어떤 숫자(1,2,4)가 들어갈지도 알아내야 합니다. 경험적으로 시행착오를 겪어보았고 다음과 같은 규칙을 발견하였습니다. n을 124로 표현했을 때 자릿수-1 만큼 3의 제곱배를 계속해서 빼줍니다. 그러면 n을 124로 표현했을 때 자릿수에서의 순위가 나옵니다.

 

두자리수

n이 

4의 경우 4-3-1=0 

5의 경우 5-3-1=1

9의 경우 9-3-1=5

 

0 = 0*3^1 + 0*3^0 => 11

1 = 0*3^1 + 1*3^0 => 12

5 = 1*3^1 + 2*3^0 => 24

 

이를 통해 3의 제곱배 앞에 붙은 0, 1, 2에 따라 숫자가 1,2,4로 정해진다는 것을 알 수 있습니다. 

 

 

세자리수 

n이

13의 경우 13-9-3-1=0

17의 경우 17-9-3-1=4

24의 경우 17-9-3-1=11

 

0 = 0*3^2 + 0*3^1 + 0*3^0 => 111

4 = 0*3^2 + 1*3^1 + 1*3^0 => 122

11 = 1*3^2 + 0*3^1 + 2*3^0 => 214

솔루션

def solution(n):
    answer = ''
    count=0
    while n-3**count>=0:
        n=n-3**count
        count+=1
    
    for _ in range(count):
        remain = n % 3
        n = n //3
        if remain == 0:
            answer+='1'
        elif remain == 1:
            answer+='2'
        elif remain == 2:
            answer+='4'
        
    return answer[::-1]
728x90
728x90

문제 설명

다음 규칙을 지키는 문자열을 올바른 괄호 문자열이라고 정의합니다.

  • (), [], {} 는 모두 올바른 괄호 문자열입니다.
  • 만약 A가 올바른 괄호 문자열이라면, (A), [A], {A} 도 올바른 괄호 문자열입니다. 예를 들어, [] 가 올바른 괄호 문자열이므로, ([]) 도 올바른 괄호 문자열입니다.
  • 만약 A, B가 올바른 괄호 문자열이라면, AB 도 올바른 괄호 문자열입니다. 예를 들어, {}  ([]) 가 올바른 괄호 문자열이므로, {}([]) 도 올바른 괄호 문자열입니다.

대괄호, 중괄호, 그리고 소괄호로 이루어진 문자열 s가 매개변수로 주어집니다. 이 s를 왼쪽으로 x (0 ≤ x < (s의 길이)) 칸만큼 회전시켰을 때 s가 올바른 괄호 문자열이 되게 하는 x의 개수를 return 하도록 solution 함수를 완성해주세요.


제한사항

  • s의 길이는 1 이상 1,000 이하입니다.

입출력 예

 s                                                                                                         result

"[](){}" 3
"}]()[{" 2
"[)(]" 0
"}}}" 0

입출력 예 설명

입출력 예 #1

  • 다음 표는 "[](){}" 를 회전시킨 모습을 나타낸 것입니다.

xs를 왼쪽으로 x칸만큼 회전올바른 괄호 문자열?

0 "[](){}" O
1 "](){}[" X
2 "(){}[]" O
3 "){}[](" X
4 "{}[]()" O
5 "}[](){" X
  • 올바른 괄호 문자열이 되는 x가 3개이므로, 3을 return 해야 합니다.

입출력 예 #2

  • 다음 표는 "}]()[{" 를 회전시킨 모습을 나타낸 것입니다.

xs를 왼쪽으로 x칸만큼 회전올바른 괄호 문자열?

0 "}]()[{" X
1 "]()[{}" X
2 "()[{}]" O
3 ")[{}](" X
4 "[{}]()" O
5 "{}]()[" X
  • 올바른 괄호 문자열이 되는 x가 2개이므로, 2를 return 해야 합니다.

입출력 예 #3

  • s를 어떻게 회전하더라도 올바른 괄호 문자열을 만들 수 없으므로, 0을 return 해야 합니다.

입출력 예 #4

  • s를 어떻게 회전하더라도 올바른 괄호 문자열을 만들 수 없으므로, 0을 return 해야 합니다.

전략

괄호의 짝이 올바르면 stack에 쌓인 괄호를 pop하여 stack을 비워줍니다. stack이 최종적으로 비워지면 count를 한 개씩 늘려줍니다.  

솔루션

def solution(s):
    count = 0
    right={')':'(','}':'{',']':'['}
    for i in range(len(s)):
        stack=[]
        words = s[i:]+s[:i]
        for j in range(len(words)):
            if len(stack) ==0:
                stack.append(words[j])
            elif right.get(words[j],1) == stack[-1]:
                stack.pop()
            else:
                stack.append(words[j])
        if len(stack) == 0:
            count+=1
    return count
728x90
728x90

문제 설명

rows x columns 크기인 행렬이 있습니다. 행렬에는 1부터 rows x columns까지의 숫자가 한 줄씩 순서대로 적혀있습니다. 이 행렬에서 직사각형 모양의 범위를 여러 번 선택해, 테두리 부분에 있는 숫자들을 시계방향으로 회전시키려 합니다. 각 회전은 (x1, y1, x2, y2)인 정수 4개로 표현하며, 그 의미는 다음과 같습니다.

  • x1 행 y1 열부터 x2 행 y2 열까지의 영역에 해당하는 직사각형에서 테두리에 있는 숫자들을 한 칸씩 시계방향으로 회전합니다.

다음은 6 x 6 크기 행렬의 예시입니다.

이 행렬에 (2, 2, 5, 4) 회전을 적용하면, 아래 그림과 같이 2행 2열부터 5행 4열까지 영역의 테두리가 시계방향으로 회전합니다. 이때, 중앙의 15와 21이 있는 영역은 회전하지 않는 것을 주의하세요.

행렬의 세로 길이(행 개수) rows, 가로 길이(열 개수) columns, 그리고 회전들의 목록 queries가 주어질 때, 각 회전들을 배열에 적용한 뒤, 그 회전에 의해 위치가 바뀐 숫자들 중 가장 작은 숫자들을 순서대로 배열에 담아 return 하도록 solution 함수를 완성해주세요.


제한사항

  • rows는 2 이상 100 이하인 자연수입니다.
  • columns는 2 이상 100 이하인 자연수입니다.
  • 처음에 행렬에는 가로 방향으로 숫자가 1부터 하나씩 증가하면서 적혀있습니다.
    • 즉, 아무 회전도 하지 않았을 때, i 행 j 열에 있는 숫자는 ((i-1) x columns + j)입니다.
  • queries의 행의 개수(회전의 개수)는 1 이상 10,000 이하입니다.
  • queries의 각 행은 4개의 정수 [x1, y1, x2, y2]입니다.
    • x1 행 y1 열부터 x2 행 y2 열까지 영역의 테두리를 시계방향으로 회전한다는 뜻입니다.
    • 1 ≤ x1 < x2 ≤ rows, 1 ≤ y1 < y2 ≤ columns입니다.
    • 모든 회전은 순서대로 이루어집니다.
    • 예를 들어, 두 번째 회전에 대한 답은 첫 번째 회전을 실행한 다음, 그 상태에서 두 번째 회전을 실행했을 때 이동한 숫자 중 최솟값을 구하면 됩니다.

입출력 예시

rows           columns  queries                                                                                  result

6 6 [[2,2,5,4],[3,3,6,6],[5,1,6,3]] [8, 10, 25]
3 3 [[1,1,2,2],[1,2,2,3],[2,1,3,2],[2,2,3,3]] [1, 1, 5, 3]
100 97 [[1,1,100,97]] [1]

입출력 예 설명

입출력 예 #1

  • 회전을 수행하는 과정을 그림으로 표현하면 다음과 같습니다.

입출력 예 #2

  • 회전을 수행하는 과정을 그림으로 표현하면 다음과 같습니다.

입출력 예 #3

  • 이 예시에서는 행렬의 테두리에 위치한 모든 칸들이 움직입니다. 따라서, 행렬의 테두리에 있는 수 중 가장 작은 숫자인 1이 바로 답이 됩니다.

 

전략

행렬 회전은 행렬의 요소 중 하나를 기준점으로 설정하고, 시계 방향으로 값을 밀어줍니다. 우선, 기준점을 (x1,y1)으로 설정합니다. 시계방향으로 회전하기 위해서 오른쪽으로 (y2-y1)만큼, 아래쪽으로 (x2-x1)만큼, 왼쪽으로 (y2-y1)만큼,  

위쪽으로 (x2-x1)만큼 이동해주면 됩니다. 밀어줄 때 자료의 손실을 막아주기 위하여 잠시 저장해주는 변수를 생성합니다. 이 때, 큐를 사용합니다. 회전하면서 다음에 위치한 요소가 저장한 값을 큐에 저장합니다. 큐에 먼저 저장된 값을 pop하여 다음 위치로 값을 밀어줍니다. 이런 방식을 반복하여 줍니다. 회전하는 값들 중에 최소값을 찾아야 하므로 minimum 변수를 생성하여 값이 작을 때마다 minimum을 업데이트합니다.

 

솔루션

def solution(rows, columns, queries):
    answer = []
    matrix=[[i+columns*j for i in range(1,columns+1)] for j in range(rows) ]

    for x1, y1, x2, y2 in queries:
        w, h=y2-y1, x2-x1
        x,y= x1-1,y1-1
        temp=[matrix[x][y]]
        minimum=10000
        for _ in range(w):
            temp.append(matrix[x][y+1])
            num = temp.pop(0)
            if num < minimum:
                minimum = num
            matrix[x][y+1]=num
            y+=1
            
        for _ in range(h):
            temp.append(matrix[x+1][y])
            num = temp.pop(0)
            if num < minimum:
                minimum = num
            matrix[x+1][y]=num
            x+=1
            
        for _ in range(w):
            temp.append(matrix[x][y-1])
            num = temp.pop(0)
            if num < minimum:
                minimum = num
            matrix[x][y-1]=num
            y-=1

        for _ in range(h):
            temp.append(matrix[x-1][y])
            num = temp.pop(0)
            if num < minimum:
                minimum = num
            matrix[x-1][y]=num
            x-=1
        answer.append(minimum)
    return answer
728x90
728x90

문제 설명

아래와 같이 5와 사칙연산만으로 12를 표현할 수 있습니다.

12 = 5 + 5 + (5 / 5) + (5 / 5)
12 = 55 / 5 + 5 / 5
12 = (55 + 5) / 5

5를 사용한 횟수는 각각 6,5,4 입니다. 그리고 이중 가장 작은 경우는 4입니다.
이처럼 숫자 N과 number가 주어질 때, N과 사칙연산만 사용해서 표현 할 수 있는 방법 중 N 사용횟수의 최솟값을 return 하도록 solution 함수를 작성하세요.

제한사항

  • N은 1 이상 9 이하입니다.
  • number는 1 이상 32,000 이하입니다.
  • 수식에는 괄호와 사칙연산만 가능하며 나누기 연산에서 나머지는 무시합니다.
  • 최솟값이 8보다 크면 -1을 return 합니다.

입출력 예

N                                             number                                                   return

5 12 4
2 11 3

입출력 예 설명

예제 #1
문제에 나온 예와 같습니다.

예제 #2
11 = 22 / 2와 같이 2를 3번만 사용하여 표현할 수 있습니다.

 

 

이 문제는 스스로 몇시간동안 삽질을 해보아도 도무지 해결방안이 생각이 안나서 다른 사람의 글을 참고하였다. 참조한 블로그는 아래쪽에 써두었다. N을 8개까지 사용가능하다는 점에서 N을 1개 사용했을 때부터 차례대로 사칙연산을 해본다.

 

N이 1개일 때,

5

 

N이 1개라면 5만 만들어진다.

 

N이 2개일 때,

55, 5+5, 5-5, 5/5, 5*5

-> 55, 10, 0, 1, 25

 

N이 2개일 때는 2가지 종류가 있다.

N을 1개 덧붙이는 값 : 55

N이 1개일 때의 집합끼리 사칙연산한 값:  10, 0, 1, 25

 

 

N이 3개일 때,

 

N이 3개일 때는 N이 1개일 때와, 2개일 때에 대하여 사칙연산을 진행한다. 더하기 연산과 곱셈 연산은 순서가 상관이 없지만, 뺄셈 연산과 나눗셈 연산은 순서가 상관이 있으므로 반대의 경우에도 연산을 한 번 해준다.

 

N이 4개일 때,

4=1+3=2+2 이므로 N이 1개일 때와 3개일때 / N이 2개일 때와 2개일 때에 대하여 사칙연산을 해준다.

 

솔루션

def solution(N, number):
    answer = 0
    calculate=[set(),set(),set(),set(),set(),set(),set(),set(),set()]
    for i in range(1,9): # N은 8개까지 사용가능
        calculate[i].add(int(str(N)*i)) 
        for j in range(1,i): # 각 단계에서 짝을 맞추어 i개로 합치기
            for k in calculate[j]:
                for l in calculate[i-j]:
                    calculate[i].add(k+l)
                    calculate[i].add(k-l)
                    calculate[i].add(k*l)
                    if l != 0:
                        calculate[i].add(k/l)
        if number in calculate[i]:
            return i
    return -1

 

 

 

 

   

 

참조)

https://gurumee92.tistory.com/164

728x90
728x90

문제 설명

오픈채팅방

카카오톡 오픈채팅방에서는 친구가 아닌 사람들과 대화를 할 수 있는데, 본래 닉네임이 아닌 가상의 닉네임을 사용하여 채팅방에 들어갈 수 있다.

신입사원인 김크루는 카카오톡 오픈 채팅방을 개설한 사람을 위해, 다양한 사람들이 들어오고, 나가는 것을 지켜볼 수 있는 관리자창을 만들기로 했다. 채팅방에 누군가 들어오면 다음 메시지가 출력된다.

"[닉네임]님이 들어왔습니다."

채팅방에서 누군가 나가면 다음 메시지가 출력된다.

"[닉네임]님이 나갔습니다."

채팅방에서 닉네임을 변경하는 방법은 다음과 같이 두 가지이다.

  • 채팅방을 나간 후, 새로운 닉네임으로 다시 들어간다.
  • 채팅방에서 닉네임을 변경한다.

닉네임을 변경할 때는 기존에 채팅방에 출력되어 있던 메시지의 닉네임도 전부 변경된다.

예를 들어, 채팅방에 "Muzi"와 "Prodo"라는 닉네임을 사용하는 사람이 순서대로 들어오면 채팅방에는 다음과 같이 메시지가 출력된다.

"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."

채팅방에 있던 사람이 나가면 채팅방에는 다음과 같이 메시지가 남는다.

"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다."

Muzi가 나간후 다시 들어올 때, Prodo 라는 닉네임으로 들어올 경우 기존에 채팅방에 남아있던 Muzi도 Prodo로 다음과 같이 변경된다.

"Prodo님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Prodo님이 나갔습니다."
"Prodo님이 들어왔습니다."

채팅방은 중복 닉네임을 허용하기 때문에, 현재 채팅방에는 Prodo라는 닉네임을 사용하는 사람이 두 명이 있다. 이제, 채팅방에 두 번째로 들어왔던 Prodo가 Ryan으로 닉네임을 변경하면 채팅방 메시지는 다음과 같이 변경된다.

"Prodo님이 들어왔습니다."
"Ryan님이 들어왔습니다."
"Prodo님이 나갔습니다."
"Prodo님이 들어왔습니다."

채팅방에 들어오고 나가거나, 닉네임을 변경한 기록이 담긴 문자열 배열 record가 매개변수로 주어질 때, 모든 기록이 처리된 후, 최종적으로 방을 개설한 사람이 보게 되는 메시지를 문자열 배열 형태로 return 하도록 solution 함수를 완성하라.

 

제한사항

  • record는 다음과 같은 문자열이 담긴 배열이며, 길이는 1 이상 100,000 이하이다.
  • 다음은 record에 담긴 문자열에 대한 설명이다.
    • 모든 유저는 [유저 아이디]로 구분한다.
    • [유저 아이디] 사용자가 [닉네임]으로 채팅방에 입장 - "Enter [유저 아이디] [닉네임]" (ex. "Enter uid1234 Muzi")
    • [유저 아이디] 사용자가 채팅방에서 퇴장 - "Leave [유저 아이디]" (ex. "Leave uid1234")
    • [유저 아이디] 사용자가 닉네임을 [닉네임]으로 변경 - "Change [유저 아이디] [닉네임]" (ex. "Change uid1234 Muzi")
    • 첫 단어는 Enter, Leave, Change 중 하나이다.
    • 각 단어는 공백으로 구분되어 있으며, 알파벳 대문자, 소문자, 숫자로만 이루어져있다.
    • 유저 아이디와 닉네임은 알파벳 대문자, 소문자를 구별한다.
    • 유저 아이디와 닉네임의 길이는 1 이상 10 이하이다.
    • 채팅방에서 나간 유저가 닉네임을 변경하는 등 잘못 된 입력은 주어지지 않는다.

입출력 예

record                                                                     result

["Enter uid1234 Muzi", "Enter uid4567 Prodo","Leave uid1234","Enter uid1234 Prodo","Change uid4567 Ryan"] ["Prodo님이 들어왔습니다.", "Ryan님이 들어왔습니다.", "Prodo님이 나갔습니다.", "Prodo님이 들어왔습니다."]

 

솔루션

from collections import defaultdict

def solution(record):
    answer,data = [],[]
    dic=defaultdict(str)
    
    for i in range(len(record)):
        data.append(record[i].split(' '))
        if data[i][0] == 'Change':
            dic[data[i][1]]=data[i][2]
        elif data[i][0] == 'Enter':
            dic[data[i][1]]=data[i][2]
            
    for i in range(len(record)):
        if data[i][0] == 'Enter':
            answer.append('{}님이 들어왔습니다.'.format(dic[data[i][1]]))
        if data[i][0] == 'Leave':
            answer.append('{}님이 나갔습니다.'.format(dic[data[i][1]]))
            
    return answer
728x90
728x90

문제 설명

가로 길이가 Wcm, 세로 길이가 Hcm인 직사각형 종이가 있습니다. 종이에는 가로, 세로 방향과 평행하게 격자 형태로 선이 그어져 있으며, 모든 격자칸은 1cm x 1cm 크기입니다. 이 종이를 격자 선을 따라 1cm × 1cm의 정사각형으로 잘라 사용할 예정이었는데, 누군가가 이 종이를 대각선 꼭지점 2개를 잇는 방향으로 잘라 놓았습니다. 그러므로 현재 직사각형 종이는 크기가 같은 직각삼각형 2개로 나누어진 상태입니다. 새로운 종이를 구할 수 없는 상태이기 때문에, 이 종이에서 원래 종이의 가로, 세로 방향과 평행하게 1cm × 1cm로 잘라 사용할 수 있는 만큼만 사용하기로 하였습니다.
가로의 길이 W와 세로의 길이 H가 주어질 때, 사용할 수 있는 정사각형의 개수를 구하는 solution 함수를 완성해 주세요.

 

제한사항

  • W, H : 1억 이하의 자연수

입출력 예

   W       H      result

8 12 80

입출력 예 설명

입출력 예 #1
가로가 8, 세로가 12인 직사각형을 대각선 방향으로 자르면 총 16개 정사각형을 사용할 수 없게 됩니다. 원래 직사각형에서는 96개의 정사각형을 만들 수 있었으므로, 96 - 16 = 80 을 반환합니다.

예시1

전략

Fig1. 직사각형1

직사각형 종이에 대각선을 그었을 때, 최대공약수만큼의 크기를 가진 직사각형을 추출합니다. 이 직사각형은 양 귀퉁이가 연결되어 있는 것을 볼 수 있습니다. Fig1의 직사각형의 가로 세로를 각각 gcd_w, gcd_h라고 가정하였을 때 흰색 직사각형의 개수는 (gcd_w + gcd_h - 1)입니다.    

Fig2 gcd_h 방향 직사각형 개수
Fig3 gcd_w 방향 직사각형 개수
Fig4 중복된 부분

Fig 2의 경우 대각선을 가로지르기 위해서는 모든 행을 가로질러야 합니다. 가로 세로의 공약수가 없는 직사각형의 경우 직선이 한칸을 이동할 때마다 꼭지점이 아닌 선분을 반드시 가로질러야 하기 때문에 Fig3의 개수가 더해집니다. 마지막으로 공통된 부분 첫번째 사각형을 없애주기 위하여 Fig4를 제거합니다. 

 

 

솔루션

import math

def solution(w,h):
    return w*h-(w+h-math.gcd(w,h))

 

참조) https://leedakyeong.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%A9%80%EC%A9%A1%ED%95%9C-%EC%82%AC%EA%B0%81%ED%98%95-in-python

728x90
728x90

프로젝트 개요

물체를 자동 분류하여 물체의 모양에 맞게 물체를 집는 스마트 그리퍼 제작

 

우선 핸드폰 카메라를 사용하여 비전 인식을 하였습니다. 카메라를 통해 들어온 데이터를 컴퓨터를 통해 제어기인 opencr로 전달하였습니다. 그리고 opencr을 이용해 그리퍼에 사용된 모터들을 각 모드에 맞게 구동하였습니다. 이 상태에 대해 피드백을 지속적으로 진행하여 카메라에 잡힌 물체가 바뀌면 그 형상에 맞게 그리퍼의 모션을 바뀌게 하였습니다.

 

오브젝트

 

1) Data Augmentation

원본 데이터 약 600-> 1550

Augmentations

90° Rotate: Clockwise, Counter-Clockwise, Upside Down

Crop: 0% Minimum Zoom, 17% Maximum Zoom

Rotation: Between -45° and +45°

Brightness: Between -25% and +25%

Exposure: Between -25% and +25%

영상인식으로 3가지 형상 구체, 원기둥, 직사각형 오브젝트를 분류하기 위하여 데이터를 수집하였습니다. 사진 혹은 영상촬영을 통해 각 오브젝트에 대한 이미지 데이터 약 600장을 수집하였습니다. 3개의 오브젝트마다 균형있게 200장씩을 수집하였고, 각 클래스를 직사각형 모양으로 라벨링하였습니다. 모델이 과적합을 피하고 좀 더 학습을 잘 하기 위하여 원본 데이터를 어그멘테이션을 하였고, 회전, 크랍, 밝기, 노출에 각각 랜덤한 값을 넣어줍니다.

 

 

2) Yolo V5 custom training

사람은 이미지를 보면 어디에 무엇이 있는지를 한 번에 파악할 수 있습니다. 그래서 운전을 하면서도 빠르게 지나가는 바깥 환경을 빠르고 정확하게 인식할 수 있습니다. Yolo는 You only look Once의 약자로 현재까지 사람의 시각 체계와 가장 가까운 객체 탐지 모델입니다. Yolo V5단일 신경망 구조이기 때문에 계산 속도가 무척 빨라 실시간으로 사용할 수 있습니다. Yolo 모델을 앞서 전처리한 이미지데이터로 전이학습 시킵니다. 전이학습이란, 이미 훈련된 딥러닝 모델에 파인 튜닝을 적용하여 적은 데이터로도 좋은 결과값을 얻을 수 있도록 하는 기술입니다.  

 

 

3) YOLO 학습 결과

Yolo 모델이 잘 학습했는지를 나타내주는 지표인 정확도와 재현율은 0.95, 1로 우수한 성능을 갖추었습니다. 정확도란, 모델이 객체라고 예측했던 모든 bounding box들 중에 실제 정답이 있는 확률이고, 재현율이란, 실제 정답 중 모델이 정답을 맞춘 확률입니다. , 두 개의 지표가 모두 우수해야 훌륭한 모델이라 할 수 있습니다. Yolo 모델에 영상을 실시간으로 넣게 되면 출력값으로 객체의 위치를 예측하는 bounding box가 생성됩니다. 프레임 내에 cube, circle, 혹은 cylinder 객체를 예측하게 된다면 분류된 값을 시리얼 통신을 통해 OPENCR 제어기에 송신합니다.

 

왼쪽: 정확도 /오른쪽: 재현율

 

결과

소스코드

https://colab.research.google.com/drive/1L5FMv8KJbIqvWtjsHjdfDIVWgLVnSE5K

 

 

4) 그리퍼 제어

OPENCR그리퍼와 연결한 모습입니다. SUPPLY를 통하여 24V의 전압을 가해주었습니다.

 

5) 구동

원기둥.mp4
3.41MB
직사각형.mp4
4.80MB
구체.mp4
4.26MB

728x90
728x90

 

솔루션

def solution(s):
	# 홀수일 경우 짝이 맞지 않음.
    if len(s)%2==1:
        return 0
    stack=[s[0]]
    i=1
    while i<len(s):
 		# 짝이 맞을 경우 pop
        while stack and stack[-1]==s[i]:
            stack.pop()
            i+=1

        if i<len(s):
            stack.append(s[i])
            i+=1

    return 0 if len(stack) else 1

 

이 문제에서 핵심은 효율성입니다. 인덱스로 풀거나, s에 담겨 있는 중복된 데이터를 pop 하려고 한다면 효율성에서 걸리게 됩니다. 중복된 데이터를 삭제하는 전략을 짰을 경우에 s에 대해서 선형 탐색을 실시하게 되는데 최악의 경우 s에 담겨있는 데이터의 절반만큼 반복해야 합니다.(선형 탐색 한번에 중복된 걸 한번씩 찾는 경우) 또한 앞에 있는 데이터를 삭제할 경우 뒤에 있는 모든 데이터를 앞쪽으로 옮겨와야 하기 때문에 수많은 계산을 요구하게 됩니다. 데이터를 삭제하지 않고 인덱스만을 택한다고 하여도 시간 초과가 발생하게 됩니다. 그 이유는 이런 방식으로 알고리즘을 짤 경우에 O(N^2)의 복잡도가 나오기 때문입니다.  

 

 python은 1초에 2000만번 계산을 하지만 이 문제의 s는 최대 100만개의 데이터를 갖고 있습니다. 따라서 O(N^2)의 복잡도가 나오는 이러한 방식은 최선이 아닙니다 .

 

이 문제에 적합한 자료구조는 스택입니다. O(N)으로 알고리즘을 짤 수 있고 위에서 설명한 방식보다 계산량이 훨씬 줄어들기 때문에 시간을 절약해줍니다. 스택은 아래에서부터 쌓아올리고 가장 나중에 쌓은 자료를 꺼내는 구조입니다. 따라서 글자를 쌓다가 같은 글자가 2개가 쌓이면 삭제해주는 방식으로 하다보면 s에 담긴 데이터만큼의 계산을 한 이후에 결과값이 나옵니다.    

728x90

+ Recent posts