알고리즘 모임 3주차

정지호·2022년 8월 24일
0

개인 실습 진행

목록 보기
23/41


1. 백준 10814 나이순 정렬

풀이

import sys
input = sys.stdin.readline

N = int(input()) # 총 회원 수 (문자열 입력 횟수)
li = [] # 입력 받은 문자열을 공백 기준으로 쪼개어 만든 리스트를 받는 리스트
li_1 = [] # 입력 받은 문자열의 정수 부분과 인덱스로 이루어진 리스트를 받는 리스트
li_2 = [] # 회원들 나이가 증가하는 순으로, 나이가 같으면 먼저 가입한 사람이 앞에 오는 순으로 정렬된 리스트들을 가지는 리스트


for i in range(N):
    li.append(input().split()) # 입력 받은 문자열을 정수 부분과 문자부분으로 나누어 리스트를 만들고, 그 리스트를 li에 넣는다.
    li_1.append([int(li[i][0]), i]) # 입력 받은 문자열의 정수부분과 인덱스로 이루어진 리스트를 li_1에 넣는다.
    # 정수부분은 문자열이므로 int를 사용해 정수화해준다. 

result = sorted(li_1, key = lambda x : (x[0], x[1])) 
# 백준 카드 문제에서 쓴 람다를 활용한 정렬 기법을 사용하였다.
# 정수 부분을 오름차순(즉, 회원들 나이가 증가하는 순)으로 / 그리고 그 다음 인덱스를 오름차순(즉, 먼저 가입한 순)으로

for i in range(N): # 다음을 N번 반복한다
    li_2.append([str(result[i][0]), li[result[i][1]][1]]) 
    # result의 정수 부분과, li에서 result의 인덱스에 해당하는 문자열(회원 이름)로 이루어진 리스트를 li_2에 입력한다.
    # join을 써주기 위해 정수 부분은 str 함수를 사용하여 문자열로 만든다.
    print(" ".join(li_2[i])) # 공백 한 칸을 기준으로 정수 부분과 회원 이름을 합쳐준다. 그리고 이를 출력한다.
  • 사실 리스트를 하나만 사용해도 된다.
  • 그리고 굳이 join을 쓰지 말고 print(a,b) 형식을 활용해도 된다.
  • sorted 대신 sort를 쓰는게 더 좋다.

2. 백준 1431 시리얼 번호

풀이

import sys
input = sys.stdin.readline

def sum_num(s): # 모든 자릿수의 합(숫자만)을 구하는 함수
    res = 0 # 초기값 0
    for i in s: # 문자열 구성요소들 중
        if i.isdigit(): 
        # isdigit() 함수 : 단일 글자가 숫자 모양으로 생겼으면 무조건 숫자로 판별.
        # 구성요소가 숫자 모양으로 생겼으면 (즉 True이면)
            res += int(i) # 해당 요소 정수화 + res에 더해준다. => 반복하여 모든 자릿수의 합을 구함 
    return res
    
n = int(input()) # 기타의 갯수
serial = [input().rstrip() for _ in range(n)] 
# 기타의 갯수만큼 시리얼 번호들을 리스트에 넣어준다. 
# 문자열의 오르쪽 공백들을 제거해준다. 
# 인덱스가 따로 필요하지 않으므로 i대신 간단히 _를 썼다. 

serial.sort(key = lambda x:(len(x), sum_num(x), x)) #람다를 써서 정렬해준다. 1순위는 문자열의 길이가 짧은 순, 2순위는 모든 자릿수의 합(숫자만)이 작은 순, 마지막으로 사전 순이다. 사전 순은 따로 지정할 필요없다. sort가 알아서 해준다. 


for s in serial:
    print(s) # 기준에 맞게 정렬된 리스트의 요소들을 차례차례 출력한다. 
  • isdigit() 함수: 단일 글자가 숫자 모양으로 생겼으면 무조건 숫자로 판별
  • 언더스코어(_): 다음 다섯가지 상황에서 주로 쓰인다.
    1. 인터프리터(Interpreter)에서 마지막 값을 저장할 때
    2. 값을 무시하고 싶을 때 (흔히 “I don’t care”라고 부른다.)
    3. 변수나 함수명에 특별한 의미 또는 기능을 부여하고자 할 때
    4. 국제화(Internationalization, i18n)/지역화(Localization, l10n) 함수로써 사용할 때
    5. 숫자 리터럴값의 자릿수 구분을 위한 구분자로써 사용할 때

이 문제에서는 2번의 경우이다. (인덱스가 필요하지 않으므로)


3. 백준 18870 좌표 압축

풀이

import sys

input = sys.stdin.readline

n = int(input())
arr = list(map(int, input().split())) # map 함수 인자로 int 함수와 리스트를 받음. 그리고 정수화된 리스트를 다시 리스트화

arr2 = sorted(list(set(arr))) # set로 중복제거(서로 다른 좌표의 개수여야 하므로) -> list로 리스트화 -> 정렬된 리스트 복제본을 arr2로. 
dic = {arr2[i] : i for i in range(len(arr2))} 
# 딕셔너리 활용
# arr2의 요소가 key, 요소의 인덱스가 value => 인덱스가 곧  'Xi > Xj를 만족하는 서로 다른 좌표의 개수'.
# 예를 들면, 인덱스가 0이면 가장 작은 수이므로(중복이 제거된), 그 수보다 작은 좌표의 개수는 0, 즉 인덱스이다.
for i in arr:
    print(dic[i], end = ' ') # 이제 딕셔너리에서 순서대로 출력만 하면된다. 
  • set
    • set은 수학에서 이야기하는 집합과 비슷하다.
    • 순서가 없고, 집합안에서는 unique한 값을 가진다.(중복이 없다)
    • 그리고 mutable한 객체이다.
    • 중괄호를 사용하는 것은 dictionary와 비슷하지만, key가 없다.값만 존재한다.

4. 백준 2447 별찍기-10

풀이

# 프랙탈: 자기 유사성을 갖는 기하학적 구조
# 이 문제는 부분의 구조를 가지고 계속 반복하여 전체의 구조를 만드는 프렉탈 구조 문제이다. 
# '***'를 하나의 요소로 이해하자

def draw_stars(n):
  if n==1: # 재귀적인 방법으로 n을 1까지 도달하게 한 다음 ['*']를 리턴해준다. 
    return ['*']

  Stars=draw_stars(n//3)
  L=[]

  for star in Stars:
    L.append(star*3)
  for star in Stars:
    L.append(star+' '*(n//3)+star)
  for star in Stars:
    L.append(star*3)
# n이 3일 때, 총 3개의 for 문을 ['*']로 돌렸을 때, L == ['***','* *', '***']
# n이 9일 때, 총 3개의 for 문을 ['*']로 돌렸을 때, 
'''
L == [['*********',
       '* ** ** *',
       '*********',
       '***   ***',
       '* *   * *',
       '***   ***',
       '*********',
       '* ** ** *',
       '*********']
'''
  return L

N=int(input())
print('\n'.join(draw_stars(N))) # join으로 리스트의 요소를 붙인다. 이때 \n(줄바꿈)을 써서 붙여주면 된다.  
  • 진짜 어려운 문제 중 하나였다.
  • 부분의 구조를 가지고 계속 반복하여 전체의 구조를 만드는 프렉탈 구조 문제
    =>재귀함수!

5. 백준 1914 하노이탑

풀이

def f(n, a, b, c): # n 개의 원판이 a 기둥에 있을때, c기둥으로 모두 이동시키기
    if(n == 1): # 원판이 한 개면 a에서 c로 옮기고 끝
        print(a, c, sep = " ")
    else:
        f(n-1, a, c, b) #N-1개의 원판을 b번 기둥으로
        f(1, a, b, c) # a 기둥에 남은 1개의 원판을 c번 기둥으로
        f(n-1, b, a, c) # b번 기둥에 있는 N-1개의 원판을 c번 기둥으로
    
n = int(input())
print(2**n-1) #이동 횟수는 항상 2**n-1
# 점화식: n+1 번째 항 = (2* n 번째 항) + 1 / 첫번째 항 ==1 => n 번째 항 == 2**n-1
if(n <= 20):
    f(n, 1, 2, 3) # 원판이 20개 미만이면 수행과정을 다 출력해야 하므로 재귀 함수로 계속 돌려 출력한다. 
  
 # 1번 기둥에 있는 N개의 원판을 규칙에 따라 모두 3번 기둥으로 옮기는 방법
    # 1. 1번 기둥에 위치한 N개의 원판 중, 위에 있는 N-1개의 원판을 2번 기둥으로 옮긴다.
    # 2. 1번 기둥에 남아있는 1개의 원판을 3번 기둥으로 옮긴다.
    # 3. 2번 기둥에 있는 N-1개의 원판을 3번 기둥으로 옮긴다.

6. 백준 10876 중복 빼고 정렬하기

풀이

n=int(input())

arr = list(map(int,input().split())) # map 함수 사용(인자로 int 함수와 리스트가 주어진다.)
arr = list(set(arr))
# set 함수: 중괄호 사용. 하지만 딕셔너리와 다르게 value 만 있음. 순서가 없고, 중복을 허용하지 않는다. 그리고 변할 수 있는 객체이다.
# 여기서는 set 함수를 사용하여 중복을 제거했다. 그리고나서 리스트화.
arr.sort()
#리스트화 하고 순서대로 정렬 -> 이제 순서대로 출력만하면 된다. 
for i in arr:
    print(i,end=' ')
  • 좌표 압축 문제처럼 set를 활용해서 중복을 제거해준다.

7. 백준 17478 재귀함수가 뭔가요

풀이

def recursive(m):
    print("_" * (4 * (n - m)) + '"재귀함수가 뭔가요?"')
    # 재귀 함수가 한 번씩 반복될 때마다 _가 4개 늘어나므로 4*(n-m)을 썼다. 

    if not m: #0이 아닌 수는 참 => m이 0이되면 조건문을 그냥 통과하게 됨
        print("_" * (4 * (n - m)) + '"재귀함수는 자기 자신을 호출하는 함수라네"')
        print("_" * (4 * (n - m)) + "라고 답변하였지.")
        return # None 반환 및 종료!

    print("_" * (4 * (n - m)) + '"잘 들어보게. 옛날옛날 한 산 꼭대기에 이세상 모든 지식을 통달한 선인이 있었어.')
    print("_" * (4 * (n - m)) + "마을 사람들은 모두 그 선인에게 수많은 질문을 했고, 모두 지혜롭게 대답해 주었지.")
    print("_" * (4 * (n - m)) + '그의 답은 대부분 옳았다고 하네. 그런데 어느 날, 그 선인에게 한 선비가 찾아와서 물었어."')
    recursive(m - 1) # "라고 답변하였지."가 재귀문과 맞아야 하므로
    print("_" * (4 * (n - m)) + "라고 답변하였지.")


n = int(input())
print("어느 한 컴퓨터공학과 학생이 유명한 교수님을 찾아가 물었다.")
recursive(n)

출처
https://sohee-dev.tistory.com/15
https://alpyrithm.tistory.com/137
https://wikidocs.net/16044

profile
정지호

0개의 댓글