링크 : https://www.acmicpc.net/problem/10451


순열 사이클

문제

1부터 N까지 정수 N개로 이루어진 순열을 나타내는 방법은 여러 가지가 있다. 예를 들어, 8개의 수로 이루어진 순열 (3, 2, 7, 8, 1, 4, 5, 6)을 배열을 이용해 표현하면 \(\begin{pmatrix} 1 & 2 &3&4&5&6&7&8 \\ 3& 2&7&8&1&4&5&6 \end{pmatrix}\) 와 같다. 또는, Figure 1과 같이 방향 그래프로 나타낼 수도 있다.

순열을 배열을 이용해 \(\begin{pmatrix} 1 & \dots & i & \dots &n \\ \pi_1& \dots& \pi_i & \dots & \pi_n \end{pmatrix}\) 로 나타냈다면, i에서 πi로 간선을 이어 그래프로 만들 수 있다.

Figure 1에 나와있는 것 처럼, 순열 그래프 (3, 2, 7, 8, 1, 4, 5, 6) 에는 총 3개의 사이클이 있다. 이러한 사이클을 "순열 사이클" 이라고 한다.

N개의 정수로 이루어진 순열이 주어졌을 때, 순열 사이클의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 순열의 크기 N (2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄에는 순열이 주어지며, 각 정수는 공백으로 구분되어 있다.

출력

각 테스트 케이스마다, 입력으로 주어진 순열에 존재하는 순열 사이클의 개수를 출력한다.


풀이

n = int(input())

# 바로 바꾼 버젼

for _ in range(n):
    t = int(input())
    adj_lst = [0] + list(map(int,input().strip().split()))
    count = 0
    for i in range(1,t+1):
        # 순환하는 대로 가고 싶으니 현재 값을 지정함
        now_num = i
        if adj_lst[i]: # 값이 0이라면 순환하면 안 됨
            while True: # 순환 함
                next_num = adj_lst[now_num] # 다음 값을 가져오고
                adj_lst[now_num] = 0 # 현재 값은 0이 됨
                if next_num == 0: # 만약 다음값이 0 이라면 순환 끝 종료
                    count += 1
                    break
                # 만약 순환 종료 되지 않으면 다음 값이 현재 값이 되고 순환함
                now_num = next_num
    print(count)
n = int(input())

# visited를 기록하는 버젼
# 이게 더 빠르다

for _ in range(n):
    t = int(input())
    adj_lst = [0] + list(map(int,input().strip().split()))
    visited = [0] * (t+1)
    count = 0
    for i in range(1,t+1):
        # 순환하는 대로 가고 싶으니 현재 값을 지정함
        now_num = i
        if visited[i] == 0:
            while True: # 방문하지 않았다면 순환함
                next_num = adj_lst[now_num] # 방문할 장소 기록
                visited[now_num] = 1 # 현재는 방문한 걸로 기록
                if visited[next_num] == 1:
                    count += 1
                    break
                # 만약 순환 종료 되지 않으면 다음 값이 현재 값이 되고 순환함
                now_num = next_num
    print(count)

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

개념 - 동주햄에게 배운 개념

인접 행렬, 인접 리스트 예시, 유향 그래프
# 인접 리스트를 이용한 그래프 탐색
found link(root) 1
    visit 1
        found link 2
            visit 2
                found link 3
                    visit 3
                        link not found
                link not found
        found link 3
            already visited 
        found link 4
            visit 4
                found link 3
                    already visited
                link not found
        link not found 
end
더보기

# 수행 시간 계산 - 정점의 제곱
visit : 4 # 노드의 개수만큼 visit함
found link : 5 + 1 # 링크의 개수만큼 found함
link not found : 4 # 노드 개수만큼 link not found
already visited : 2 #  

정점의 개수 : N = 4, 간선의 개수 : M = 5 

수행시간 : T = visit +  found link +link not found + already visited 
             = (visit + aleardy visited) + found link + link not found
             = 2 * found link + link not found
             = 2 * (M+1) + N
             => O(M+N)

# 인접 행렬을 이용한 그래프 탐색
visited 1: o
visited 2: o
visited 3: o
visited 4: x

found link(root) 1
    visit 1
        link not found 1
        found link 2
            visit 2
                link not found 1
                link not found 2
                found link 3
                    visit 3
                        link not found 1
                        link not found 2
                        link not found 3
                        link not found 4
                link not found 4
        found link 3
            already visited
        found link 4
            visit 4
                link not found 1
                link not found 2
                found link 3
                    already visited
                link not found 4
end
더보기

# 수행 시간 계산 - 정점의 제곱
found link : 6
link not found : 11
visit + already visited = found link 
found link + link not found = visit * n 

풀이

A, B, C = map(int,input().split())

# G[x] = [, , , ,]: x에 연결된 정점들
adj_lst = [[] for _ in range(11)]
adj_mat = [[0] * 11 for _ in range(11)]


for x in range(1,11): 
    for y in range(1,11):
        if A*x + B*y == C:
            adj_lst[x].append(y) # 인접 리스트를 만드는 방법
            adj_mat[x][y] = 1 # 인접 행렬을 만드는 방법
            
def print_adj_mat(adj_mat):         
    for i in range(1,11): # 인접 행렬로 출력하는 방식
        conn = [j for j in range(1,11) if adj_mat[i][j] == 1]
        if conn:
            print(*conn)
        else:
            print(0)

def print_adj_lst(adj_lst):
    for i in range(1,11):
        if adj_lst[i]:
            print(*adj_lst[i])
        else:
            print(0)

 

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

 

11279번 : 최대 힙 

문제

 

널리 잘 알려진 자료구조 중 최대 힙이 있다. 최대 힙을 이용하여 다음과 같은 연산을 지원하는 프로그램을 작성하시오.

  1. 배열에 자연수 x를 넣는다.
  2. 배열에서 가장 큰 값을 출력하고, 그 값을 배열에서 제거한다. 

프로그램은 처음에 비어있는 배열에서 시작하게 된다.

입력

첫째 줄에 연산의 개수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0이라면 배열에서 가장 큰 값을 출력하고 그 값을 배열에서 제거하는 경우이다. 입력되는 자연수는 231보다 작다.

출력

입력에서 0이 주어진 횟수만큼 답을 출력한다. 만약 배열이 비어 있는 경우인데 가장 큰 값을 출력하라고 한 경우에는 0을 출력하면 된다.

알고리즘 분류

 


알고리즘 풀이

힙 : 힙은 최솟갑과 최대값을 빠르게 찾기 위한 완전이진트리를 기본으로 한다.

출처: https://www.geeksforgeeks.org/heap-data -structure/minheapandmaxheap/

최대 힙: 부모 노드의 키 값이 자식노드보다 항상 큰 키 값을 가짐

최소 힙: 부모 노드의 키 값이 자식노드보다 항상 작은 키 값을 가짐

 

python의 내장된 heapq 라이브러리는 최소 힙을 기본으로 한다. 여기서는 최소힙을 통해 최대 힙을 만들면 된다.

 

heapq.heappush(heap, x) : 힙에 값을 넣는다. 그리고 최소 힙에 맞게 리스트의 처음이 최솟값으로 정렬되어있는다.

heapq.heappop(heap) : 가장 작은 값을 꺼내고 힙에서 삭제한다.

 

최소 힙이므로 최대 힙을 만들기 위해 넣어야 하는 값을 음수로 변환해서 튜플 형식으로 같이 값을 넣어주면 된다.

heapq.heappush(max_heap,(-x, x)) 이런식으로 값을 넣어주면 된다. 그리고 튜플에 2번째 값을 출력하면 된다.

 

import heapq
import sys

input = sys.stdin.readline

n = int(input())
max_heap = []

for _ in range(n):
    x = int(input())
    if x == 0:
        if max_heap:
            print(heapq.heappop(max_heap)[1])
        else:
            print(0)
    else:
        heapq.heappush(max_heap,(-x,x))
        print(max_heap)

 

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


1417번 국회의원 선거

 

문제

다솜이는 사람의 마음을 읽을 수 있는 기계를 가지고 있다. 다솜이는 이 기계를 이용해서 2008년 4월 9일 국회의원 선거를 조작하려고 한다.

다솜이의 기계는 각 사람들이 누구를 찍을 지 미리 읽을 수 있다. 어떤 사람이 누구를 찍을 지 정했으면, 반드시 선거때 그 사람을 찍는다.

현재 형택구에 나온 국회의원 후보는 N명이다. 다솜이는 이 기계를 이용해서 그 마을의 주민 M명의 마음을 모두 읽었다.

다솜이는 기호 1번이다. 다솜이는 사람들의 마음을 읽어서 자신을 찍지 않으려는 사람을 돈으로 매수해서 국회의원에 당선이 되게 하려고 한다. 다른 모든 사람의 득표수 보다 많은 득표수를 가질 때, 그 사람이 국회의원에 당선된다.

예를 들어서, 마음을 읽은 결과 기호 1번이 5표, 기호 2번이 7표, 기호 3번이 7표 라고 한다면, 다솜이는 2번 후보를 찍으려고 하던 사람 1명과, 3번 후보를 찍으려고 하던 사람 1명을 돈으로 매수하면, 국회의원에 당선이 된다.

돈으로 매수한 사람은 반드시 다솜이를 찍는다고 가정한다.

다솜이가 매수해야하는 사람의 최솟값을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 후보의 수 N이 주어진다. 둘째 줄부터 차례대로 기호 1번을 찍으려고 하는 사람의 수, 기호 2번을 찍으려고 하는 수, 이렇게 총 N개의 줄에 걸쳐 입력이 들어온다. N은 50보다 작거나 같은 자연수이고, 득표수는 100보다 작거나 같은 자연수이다.

출력

첫째 줄에 다솜이가 매수해야 하는 사람의 최솟값을 출력한다.

 

알고리즘 분류

 


알고리즘 풀이

 

다솜이의 투표 수와 나머지 투표 수의 차이를 확인해야한다.

다솜이는 나머지와 투표 수와 같거나 작으면 안 된다. 나머지의 투표 수는 다솜이보다 크거나 같으면 안 된다.

 

1. 다솜이의 투표수를 가져온다.

2. 다솜이보다 크거나 같은 나머지의 투표 수를 구한다.

3. 가장 큰 나머지의 투표 수를 -1하고 다솜이의 투표 수를 +1 한다.

4. 다솜이의 투표 수가 나머지 투표 수보다 크다면 반복을 종료 한다.

 

 

n = int(input())

dasom = int(input())
if n == 1:
    print(0)
else:
    votes = []
    for _ in range(n-1): # 다솜이의 투표 수보다 나머지 투표 수가 크거나 같은 경우만 저장
        vote_size = int(input())
        if vote_size >= dasom:
            votes.append(vote_size)
    if votes: # 다솜이의 투표수가 제일 크지 않은 경우에만 실시 = 매수할 필요가 없는 경우
        count = 0
        while True:
            dasom += 1
            votes[votes.index(max(votes))] -= 1
            count += 1
            if dasom > max(votes): # 매수를 해서 다솜이의 투표소가 제일 큰 경우 정지
                break
        print(count)
    else: # 다솜이의 투표수가 제일 크다면 매수할 필요 없음
        print(0)

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

 


 

k진수 정수의 자릿수 나누기

문제

양의 정수 n과 k가 주어진다. n을 k진수로 변환한 수를 a라고 하자. a의 각 자릿수를 0을 기준으로 나눈 결과를 집합 b라고 하자. 0이 연속으로 나와서 공백이 생기는 경우는 집합 b에 포함되지 않는다. 집합 b에 있는 수의 합을 k진수로 출력하자. 예를 들어, n = 19, k = 2이면 a = 100112, b = {1, 11}, 1 + 11 = 12, 12 = 11002이므로 1100을 출력한다.

입력

첫 번째 줄에 양의 정수 n과 k가 공백을 사이에 두고 순서대로 주어진다.

출력

첫 번째 줄에 집합 b에 있는 수의 합을 k진수로 출력한다.

제한

  • 1 ≤ n ≤ 1,000,000
  • 2 ≤ k ≤ 10

예제 입력 1 

437674 3

예제 출력 1 

22101

437674를 3진수 변환하면 a = 2110201010113이다.

2110201010113을 0을 기준으로 나누면 b = {211, 2, 1, 1, 11}이다.

b에 있는 수의 합은 211 + 2 + 1 + 1 + 11 = 226이다.

226을 3진수로 변환하면 221013이다.

예제 입력 2 

29 3

예제 출력 2 

10

29를 3진수 변환하면 a = 10023이다.

1002를 0을 기준으로 나누면 b = {1, 2}이다.

b에 있는 수의 합은 1 + 2 = 3이다.

3을 3진수로 변환하면 103이다.

예제 입력 3 

11 3

예제 출력 3 

10

11을 3진수 변환하면 a = 1023이다.

102를 0을 기준으로 나누면 b = {1, 2}이다.

b에 있는 수의 합은 1 + 2 = 3이다.

3을 3진수로 변환하면 103이다.

예제 입력 4 

3 3

예제 출력 4 

1

3을 3진수로 변환하면 a = 103이다.

10을 0을 기준으로 나누면 b = {1}이다.

b에 있는 수의 합은 1이다.

1을 3진수로 변환하면 13이다.

출처

알고리즘 분류

 


 풀이

 

사실 int(n,k)가 진수로 변경해주는 줄 알았는데 그런 건 아니어서 직접 함수로 구현해봤다.

 

  1. 값의 몫과 나머지를 구한다.
  2. 나머지는 리스트에 추가해둔다.
  3. 몫을 값으로 만들고 1,2번 과정을 n이 0보다 클 때까지 반복한다.
  4. 이후 리스트의 순서가 거꾸로이니 반대로 뒤집고 숫자로 만든다.

그리고 알고리즘 풀이 함수로 만들면 끝~

map에 lambda로 예외사항만 추가해주면 된다. 1002 숫자가 split이 되면 '1', '', '2'처럼 되는 예외사항을 추가해줬다.

# 진수 변경 함수 구현
def to_base(n,k):
    transes = []
    while n > 0:
        transes.append(str(n%k))
        n //= k
    transed_number = ''.join(transes[::-1])
    return int(transed_number)

# 알고리즘 풀이
def kbase_divide(n,k):
    # 타입 변경 후 k진수로 변환
    transed_number = to_base(n,k) 

    # 변환된 수 0을 기준으로 나눠서 리스트로 변환
    split_numbers_by_zero = str(transed_number).split('0')
    
    # 0이 남는 경우가 있음을 확인 lambda 형태로 수식 추가

    # 리스트 안에 값을 정수 형태로 바꾼 뒤 총합
    sum_split_numbers = sum(map(lambda x : int(x) if x != '' else 0 ,split_numbers_by_zero))

    # 다 합친 수를 k진수로 변환함
    transed_split_numbers = to_base(sum_split_numbers, k)

    print(transed_split_numbers)
    
if __name__ == "__main__":
    n, k = map(int,input().split())
    kbase_divide(n,k)

데이터엔지니어 취업 특강을 들으면서 python의 GIL이라는 단어를 처음 들었다.


python은 Global Interface Lock이 있어서 특정 시점에 싱글 스레드만 사용 가능하도록 제한하는 기능이다.

 

GIL은 파이썬의 코드를 여러 개의 스레드가 동시에 읽지 못하도록 만든다.

 

그 이유가 뭐냐면, 파이썬은 참조를 통해 변수에 id를 준다. 다른 언어는 변수에 값에 주소를 줘서 할당한다.

비유를 하면 숫자에 티켓을 붙여서 변수로 불러오는데, 이후 이 티켓의 개수를 세는 레퍼런스 카운팅을 실시한다.


근데, 여러 개의 쓰레드가스레드가 동시에 티켓의 개수를 세 버리면 여러 스레드가 티켓의 개수를 세서 동기화가 일어나지 않는 Race Condition이 발생할 수 있다.

 

여러 스레드가 공유 데이터를 수정해서 동기화가 발생하지 않을 수 있기 때문에 파이썬에서는 이를 막아둔 것이다.

 

그래서 이번 파이썬 3.13에서는 GIL을 풀 수 있는 기능을 실험적으로 추가해 봤다. 내가 멀티 스레드를 통해 속도를 개선할 일이 있을까 싶지많은 신기한 기능이라서 한 번 올려보았다.

 

https://docs.python.org/3/whatsnew/3.13.html#free-threaded-cpython

 

What’s New In Python 3.13

Editors, Adam Turner and Thomas Wouters,. This article explains the new features in Python 3.13, compared to 3.12. Python 3.13 was released on October 7, 2024. For full details, see the changelog. ...

docs.python.org

 

Python 3.13 새 기능 및 주요 변경점 요약 (2024년 10월 7일 출시)

1.  인터프리터 개선  
    • 새로운 상호작용(interactive) 인터프리터 도입:  
    • 멀티라인 편집 및 명령 기록 유지.  
    • 색상이 기본 활성화된 오류 메시지와 도움말.  
    • 키보드 단축키(F1~F3)를 통한 편리한 명령 실행.  
    • 오류 메시지 개선:  
    • 표준 라이브러리와 충돌하는 스크립트 이름에 대한 경고.  
    • 잘못된 키워드 인자 사용 시 올바른 제안을 포함한 메시지.
2.  Free-threaded CPython (PEP 703):  
    • GIL(Global Interpreter Lock) 없이 실행 지원(실험적 기능).  
    • 멀티코어 CPU에서 성능 향상 가능.  
    • 새로운 sys.\_is\_gil\_enabled() 함수로 GIL 활성화 상태 확인 가능.
3.  JIT(Just-In-Time) 컴파일러 (PEP 744):  
    • 실험적으로 도입된 JIT 컴파일러.  
    • 실행 속도는 일부 프로그램에서 향상 가능.
4.  타입 시스템 개선:  
    • PEP 696: TypeVar와 같은 타입 매개변수에 기본값 지원.  
    • PEP 702: warnings.deprecated() 데코레이터 추가.  
    • PEP 705: TypedDict 항목을 읽기 전용으로 설정하는 ReadOnly 도입.

주요 표준 라이브러리 변경점

1.  argparse:  
    • 명령줄 옵션과 인자를 deprecated 처리할 수 있는 기능 추가.
2.  base64:  
    • 새로운 Z85 인코딩/디코딩 함수 추가.
3.  dbm:  
    • SQLite를 기본 백엔드로 사용하는 dbm.sqlite3 추가.
4.  os:  
    • 타이머 알림 파일 디스크립터와 관련된 Linux 기능 추가.
5.  ssl:  
    • 기본 검증 설정 강화(보안 향상).
6.  copy:  
    • 객체 복사를 위한 copy.replace() 함수 추가.

보안 개선  
• SSL 기본 컨텍스트에서 강력한 인증 플래그(VERIFY\_X509\_PARTIAL\_CHAIN, VERIFY\_X509\_STRICT) 활성화.  
• 보안 취약점 방지를 위한 경고 및 동작 개선.

중요 제거 사항

1.  PEP 594:  
    • 더 이상 사용되지 않는 19개의 표준 라이브러리 모듈 제거:  
    • 예: cgi, telnetlib, pipes, sunau 등.
2.  2to3 도구:  
    • Python 2를 Python 3으로 변환하던 도구 제거.

이외 개선 사항

1.  표준 문법 개선:  
    • 클래스 정의의 첫 번째 줄 번호를 기록하는 **firstlineno** 속성 추가.  
    • locals() 반환값을 조작할 때의 동작 명확화(PEP 667).
2.  성능 최적화:  
    • 여러 표준 라이브러리 모듈의 가져오기 속도 개선.  
    • textwrap.indent() 속도 약 30% 향상.

+ Recent posts