링크 : 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)

+ Recent posts