# 20057 마법사 상어와 토네이도 (골드4)

유형 : 시뮬레이션 문제

point1. 달팽이 모양으로 도는 법 생각하기

point2. 흩날리는 모래 좌표

문제

마법사 상어가 토네이도를 배웠고, 오늘은 토네이도를 크기가 N×N인 격자로 나누어진 모래밭에서 연습하려고 한다. 위치 (r, c)는 격자의 r행 c열을 의미하고, A[r][c]는 (r, c)에 있는 모래의 양을 의미한다.

토네이도를 시전하면 격자의 가운데 칸부터 토네이도의 이동이 시작된다. 토네이도는 한 번에 한 칸 이동한다. 다음은 N = 7인 경우 토네이도의 이동이다.

토네이도가 한 칸 이동할 때마다 모래는 다음과 같이 일정한 비율로 흩날리게 된다.

토네이도가 x에서 y로 이동하면, y의 모든 모래가 비율과 α가 적혀있는 칸으로 이동한다. 비율이 적혀있는 칸으로 이동하는 모래의 양은 y에 있는 모래의 해당 비율만큼이고, 계산에서 소수점 아래는 버린다. α로 이동하는 모래의 양은 비율이 적혀있는 칸으로 이동하지 않은 남은 모래의 양과 같다. 모래가 이미 있는 칸으로 모래가 이동하면, 모래의 양은 더해진다. 위의 그림은 토네이도가 왼쪽으로 이동할 때이고, 다른 방향으로 이동하는 경우는 위의 그림을 해당 방향으로 회전하면 된다.

토네이도는 (1, 1)까지 이동한 뒤 소멸한다. 모래가 격자의 밖으로 이동할 수도 있다. 토네이도가 소멸되었을 때, 격자의 밖으로 나간 모래의 양을 구해보자.

입력

첫째 줄에 격자의 크기 N이 주어진다. 둘째 줄부터 N개의 줄에는 격자의 각 칸에 있는 모래가 주어진다. r번째 줄에서 c번째 주어지는 정수는 A[r][c] 이다.

출력

격자의 밖으로 나간 모래의 양을 출력한다.

import sys
input = sys.stdin.readline

# 방향 왼쪽, 아래, 오른쪽, 위
dx = [0,1,0,-1]
dy = [-1,0,1,0]

# 각 방향에 따른 비율
left = [[0,0,2,0,0],[0,10,7,1,0],[5,'a',0,0,0],[0,10,7,1,0],[0,0,2,0,0]]
down = [[0,0,0,0,0],[0,1,0,1,0], [2,7,0,7,2], [0,10,'a',10,0], [0,0,5,0,0]]
right = [[0,0,2,0,0],[0,1,7,10,0],[0,0,0,'a',5],[0,1,7,10,0],[0,0,2,0,0]]
up = [[0,0,5,0,0],[0,10,'a',10,0],[2,7,0,7,2],[0,1,0,1,0],[0,0,0,0,0]]


# d방향으로 cnt 만큼 반복하며 이동
def move(cnt, d, rate):
    global answer
    global x,y
    global array

    for _ in range(cnt+1):
        # 이동한 위치
        x += dx[d]
        y += dy[d]
        a_loca = [] # a의 좌표값 저장
        temp = 0  # 흩어진 모래의 총값
        # x,y좌표를 rate 배열 좌표 중앙에 존재하도록 이동
        a, b = x-2, y-2


        # 토네이도는 (1, 1)까지 이동한 뒤 소멸한다
        # -> 1행1열에서 소멸한다 -> 좌표값은 (0,0)임
        # (0, 0) 지점에 도달하는 경우 종료
        if x < 0 or y <0:
            # print("break")
            break

        # 모래 흩어지기
        for i in range(5):
            for j in range(5):
                if rate[i][j] !=0 and rate[i][j] !='a':
                    if -1< a+i <n and -1< b+j <n: # 격자 안에 존재할 경우
                        array[a+i][b+j] += array[x][y]*rate[i][j]//100
                    else: # 격자 밖으로 나갈 경우
                        answer += array[x][y]*rate[i][j]//100
                    # a값 구하기 위해 흠어진 모래양 temp에 더해주기
                    temp += array[x][y]*rate[i][j]//100
                elif rate[i][j] =='a': # a 좌표 기억
                    a_loca = [i,j]

        # 흩어지고 남은 모래 처리하기
        a_x,a_y = a_loca
        if -1< a+a_x < n and -1< b+a_y < n: # 격자 안에 존재할 경우 좌표에 더해줌
            array[a+a_x][b+a_y] += array[x][y] -temp
        else: # 격자 밖으로 나갈 경우 answer에 더해줌
            answer+=array[x][y] -temp
        array[x][y] = 0

n = int(input())
array = []
for i in range(n):
    array.append(list(map(int, input().split())))

x,y = n//2, n//2 # 처음 시작 좌표
answer = 0 # 격자 밖으로 나간 모래양

# 달팽이 모양으로 돌기
for i in range(n):
    if i%2 == 0:
        move(i, 0, left)
        move(i, 1, down)
    else:
        move(i, 2, right)
        move(i, 3, up)

print(answer)
반응형

#13458 시험 감독 (브론즈 2)

문제

총 N개의 시험장이 있고, 각각의 시험장마다 응시자들이 있다. i번 시험장에 있는 응시자의 수는 Ai명이다.

감독관은 총감독관과 부감독관으로 두 종류가 있다. 총감독관은 한 시험장에서 감시할 수 있는 응시자의 수가 B명이고, 부감독관은 한 시험장에서 감시할 수 있는 응시자의 수가 C명이다.

각각의 시험장에 총감독관은 오직 1명만 있어야 하고, 부감독관은 여러 명 있어도 된다.

각 시험장마다 응시생들을 모두 감시해야 한다. 이때, 필요한 감독관 수의 최솟값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 시험장의 개수 N(1 ≤ N ≤ 1,000,000)이 주어진다.

둘째 줄에는 각 시험장에 있는 응시자의 수 Ai (1 ≤ Ai ≤ 1,000,000)가 주어진다.

셋째 줄에는 B와 C가 주어진다. (1 ≤ B, C ≤ 1,000,000)

출력

각 시험장마다 응시생을 모두 감독하기 위해 필요한 감독관의 최소 수를 출력한다.

 

import math
n = int(input())
a = list(map(int, input().split()))
b,c = map(int, input().split())
count = 0

for i in a:
    i-=b
    count+=1
    if i>0 :
        count+= math.ceil(i/c)
print(count)
반응형

#20055 컨베이어 벨트 위의 로봇

유형 : 시뮬레이션 문제

point. deque를 rotate 함수를 이용해 회전시켜주기

 

문제

길이가 N인 컨베이어 벨트가 있고, 길이가 2N인 벨트가 이 컨베이어 벨트를 위아래로 감싸며 돌고 있다. 벨트는 길이 1 간격으로 2N개의 칸으로 나뉘어져 있으며, 각 칸에는 아래 그림과 같이 1부터 2N까지의 번호가 매겨져 있다.

벨트가 한 칸 회전하면 1번부터 2N-1번까지의 칸은 다음 번호의 칸이 있는 위치로 이동하고, 2N번 칸은 1번 칸의 위치로 이동한다. i번 칸의 내구도는 Ai이다. 위의 그림에서 1번 칸이 있는 위치를 "올리는 위치", N번 칸이 있는 위치를 "내리는 위치"라고 한다.

컨베이어 벨트에 박스 모양 로봇을 하나씩 올리려고 한다. 로봇은 올리는 위치에만 올릴 수 있고, 내리는 위치에서만 내릴 수 있다. 로봇은 컨베이어 벨트 위에서 스스로 이동할 수 있다. 로봇을 올리는 위치에 올리거나 로봇이 어떤 칸으로 이동하면 그 칸의 내구도는 즉시 1만큼 감소한다.

컨베이어 벨트를 이용해 로봇들을 건너편으로 옮기려고 한다. 로봇을 옮기는 과정에서는 아래와 같은 일이 순서대로 일어난다.

  1. 벨트가 각 칸 위에 있는 로봇과 함께 한 칸 회전한다. 내리는 위치에 있는 로봇은 내린다.
  2. 가장 먼저 벨트에 올라간 로봇부터, 벨트가 회전하는 방향으로 한 칸 이동할 수 있다면 이동한다. 만약 이동할 수 없다면 가만히 있는다.
    1. 로봇이 이동하기 위해서는 로봇이 내리는 위치가 아니고, 이동하려는 칸에 로봇이 없으며, 그 칸의 내구도가 1 이상 남아 있어야 한다.
  3. 올리는 위치에 있는 칸의 내구도가 0이 아니면 올리는 위치에 로봇을 올린다.
  4. 내구도가 0인 칸의 개수가 K개 이상이라면 과정을 종료한다. 그렇지 않다면 1번으로 돌아간다.

종료되었을 때 몇 번째 단계가 진행 중이었는지 구해보자. 가장 처음 수행되는 단계는 1번째 단계이다.

입력

첫째 줄에 N, K가 주어진다. 둘째 줄에는 A1, A2, ..., A2N이 주어진다.

출력

몇 번째 단계가 진행 중일때 종료되었는지 출력한다.

 

from collections import deque
import sys

input = sys.stdin.readline
n, k = map(int, input().split())
q_list = deque(map(int, input().split()))
robot_list = deque([0]*n)
answer = 1

while True:
    #1 로봇과 벨트를 이동시키고, 내리는 위치에 있는 로봇을 내린다
    q_list.rotate(1)
    robot_list.rotate(1)
    robot_list[-1] = 0 # 로봇 내려감

    # 2 벨트 위에 로봇이 존재한다면
    if sum(robot_list):
        for i in range(n-2,-1,-1): # 로봇내려가는 전부분부터(뒤에서부터) 이동시켜줌
            # 해당칸에 이동할 로봇이 있고 이동시킬 칸에 로봇이 없고 내구성이 0보다 크면 이동시켜줌(벨트 내구성-1)
            if robot_list[i] !=0 and robot_list[i+1] == 0 and q_list[i+1] > 0:
                q_list[i+1] -= 1
                robot_list[i+1] = 1
                robot_list[i] = 0
        robot_list[-1]=0 # 로봇 내려감

    # 3 올리는 위치의 내구성이 0보다 크고 로봇이 올라가있지 않다면 로봇을 올린다(벨트 내구성-1)
    if q_list[0] > 0 and robot_list[0] == 0 :
        q_list[0] -= 1
        robot_list[0] = 1

    # 4 내구도 0인 칸의 개수가 k개 이상이면 종료하고 단계를 출력한다
    cnt = 0
    if q_list.count(0) >= k:
        print(answer)
        break
    answer+=1
반응형

#20056 마법사 상어와 파이어볼 (골드5)

유형 : 시뮬레이션 문제

point 1. deque를 이용해 파이어볼 빼주기

point 2. 파이어볼을 바로 이동시키면 이동시킨 파이어볼을 또 이동시킬 수 있다. temp에 이동시킬 값을 저장해두고 이동을 마치면 array에 더해주기

 

문제

어른 상어가 마법사가 되었고, 파이어볼을 배웠다.

마법사 상어가 크기가 N×N인 격자에 파이어볼 M개를 발사했다. 가장 처음에 파이어볼은 각자 위치에서 이동을 대기하고 있다. i번 파이어볼의 위치는 (ri, ci), 질량은 mi이고, 방향은 di, 속력은 si이다. 위치 (r, c)는 r행 c열을 의미한다.

격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.

파이어볼의 방향은 어떤 칸과 인접한 8개의 칸의 방향을 의미하며, 정수로는 다음과 같다.

7 0 1
6   2
5 4 3

마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.

  1. 모든 파이어볼이 자신의 방향 di로 속력 si칸 만큼 이동한다.
    • 이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
  2. 이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
    1. 같은 칸에 있는 파이어볼은 모두 하나로 합쳐진다.
    2. 파이어볼은 4개의 파이어볼로 나누어진다.
    3. 나누어진 파이어볼의 질량, 속력, 방향은 다음과 같다.
      1. 질량은 ⌊(합쳐진 파이어볼 질량의 합)/5⌋이다.
      2. 속력은 ⌊(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)⌋이다.
      3. 합쳐지는 파이어볼의 방향이 모두 홀수이거나 모두 짝수이면, 방향은 0, 2, 4, 6이 되고, 그렇지 않으면 1, 3, 5, 7이 된다.
    4. 질량이 0인 파이어볼은 소멸되어 없어진다.

마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 구해보자.

입력

첫째 줄에 N, M, K가 주어진다.

둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다.

서로 다른 두 파이어볼의 위치가 같은 경우는 입력으로 주어지지 않는다.

출력

마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 출력한다.

 

from collections import deque
dx = [-1, -1, 0, 1, 1, 1, 0, -1]
dy = [0, 1, 1, 1, 0, -1, -1, -1]

# 파이어볼 좌표 반환
def find_fire(array):
    position = []
    for i in range(n):
        for j in range(n):
            if len(array[i][j]) != 0:
                position.append([i,j])
    return position

# 파이어볼 이동
def move_fire():
    global array
    position = find_fire(array)
    temp = []
    for x,y in position:
        for _ in range(len(array[x][y])):
            m, s, d = array[x][y].popleft()
            nx, ny = (x+dx[d]*s)%n, (y+dy[d]*s)%n
            temp.append([nx, ny, m,s,d])
    for r, c, m, s, d in temp:
        array[r][c].append([m, s, d])
    # print(array)

#파이어볼 합치기
def sum_fire():
    global array
    position = find_fire(array)
    d_list = []
    temp = []
    for x,y in position:
        sum_m, sum_s, count, even_d, odd_d = 0, 0, 0, False, False

        if len(array[x][y]) > 1:
            for _ in range(len(array[x][y])):
                m, s, d = array[x][y].popleft()
                sum_m += m
                sum_s += s
                count += 1
                if d%2 == 0:
                    even_d = True
                else:
                    odd_d = True
            r_m = int(sum_m/5)
            r_s = int(sum_s/count)

            if r_m != 0: # 질량이 0이 아닐때
                if even_d == True and odd_d == True: #홀짝 섞여있음
                    d_list = [1,3,5,7]
                else: # 모두 홀수 or 모두 짝수
                    d_list = [0,2,4,6]

                # 4방향으로 나누어진다
                for i in d_list:
                    temp.append([x, y, r_m, r_s, i])

    for r, c, m, s, d in temp:
        array[r][c].append([m, s, d])
    # print(array)

# n : 맵 크기, m : 파이어볼 개수, k : 명령횟수
n,m,k = map(int, input().split())
array = [[deque() for _ in range(n)] for _ in range(n)]
answer = 0
# r : 행, c : 열, m : 질량, s : 속력, d : 방향
for i in range(m):
    r, c, m, s, d = map(int, input().split())
    array[r - 1][c - 1].append([m, s, d])

for i in range(k):
    move_fire()
    sum_fire()

position = find_fire(array)
for x, y in position:
    for _ in range(len(array[x][y])):
        m, s, d = array[x][y].popleft()
        answer += m
print(answer)
반응형

# 19236 청소년 상어 (골드2)

유형 : DFS, 시뮬레이션

문제

아기 상어가 성장해 청소년 상어가 되었다.

4×4크기의 공간이 있고, 크기가 1×1인 정사각형 칸으로 나누어져 있다. 공간의 각 칸은 (x, y)와 같이 표현하며, x는 행의 번호, y는 열의 번호이다. 한 칸에는 물고기가 한 마리 존재한다. 각 물고기는 번호와 방향을 가지고 있다. 번호는 1보다 크거나 같고, 16보다 작거나 같은 자연수이며, 두 물고기가 같은 번호를 갖는 경우는 없다. 방향은 8가지 방향(상하좌우, 대각선) 중 하나이다.

오늘은 청소년 상어가 이 공간에 들어가 물고기를 먹으려고 한다. 청소년 상어는 (0, 0)에 있는 물고기를 먹고, (0, 0)에 들어가게 된다. 상어의 방향은 (0, 0)에 있던 물고기의 방향과 같다. 이후 물고기가 이동한다.

물고기는 번호가 작은 물고기부터 순서대로 이동한다. 물고기는 한 칸을 이동할 수 있고, 이동할 수 있는 칸은 빈 칸과 다른 물고기가 있는 칸, 이동할 수 없는 칸은 상어가 있거나, 공간의 경계를 넘는 칸이다. 각 물고기는 방향이 이동할 수 있는 칸을 향할 때까지 방향을 45도 반시계 회전시킨다. 만약, 이동할 수 있는 칸이 없으면 이동을 하지 않는다. 그 외의 경우에는 그 칸으로 이동을 한다. 물고기가 다른 물고기가 있는 칸으로 이동할 때는 서로의 위치를 바꾸는 방식으로 이동한다.

물고기의 이동이 모두 끝나면 상어가 이동한다. 상어는 방향에 있는 칸으로 이동할 수 있는데, 한 번에 여러 개의 칸을 이동할 수 있다. 상어가 물고기가 있는 칸으로 이동했다면, 그 칸에 있는 물고기를 먹고, 그 물고기의 방향을 가지게 된다. 이동하는 중에 지나가는 칸에 있는 물고기는 먹지 않는다. 물고기가 없는 칸으로는 이동할 수 없다. 상어가 이동할 수 있는 칸이 없으면 공간에서 벗어나 집으로 간다. 상어가 이동한 후에는 다시 물고기가 이동하며, 이후 이 과정이 계속해서 반복된다.

입력

첫째 줄부터 4개의 줄에 각 칸의 들어있는 물고기의 정보가 1번 행부터 순서대로 주어진다. 물고기의 정보는 두 정수 ai, bi로 이루어져 있고, ai는 물고기의 번호, bi는 방향을 의미한다. 방향 bi는 8보다 작거나 같은 자연수를 의미하고, 1부터 순서대로 ↑, ↖, ←, ↙, ↓, ↘, →, ↗ 를 의미한다.

출력

상어가 먹을 수 있는 물고기 번호의 합의 최댓값을 출력한다.

 

import copy

dx = [-1, -1, 0, 1, 1, 1, 0, -1]
dy = [0, -1, -1, -1, 0, 1, 1, 1]

# 상어 이동 후보지 반환
def shark_move(array, x,y):
    positions = []
    direction = array[x][y][1] # 상어 방향
    for i in range(1, 4):
        nx, ny = x + dx[direction], y + dy[direction]
        # 경계값 안에 있고, 물고기가 있으면 움직일 수 있다
        if 0 <= nx < 4 and 0 <= ny < 4 and 1 <= array[nx][ny][0] <= 16:
            positions.append([nx, ny])
        x, y = nx, ny
    return positions

# 물고기 위치 찾기
def find_fish(array, index): # 배열, 물고기 번호
    for i in range(4):
        for j in range(4):
            if array[i][j][0] == index:
                return (i, j)
    # 물고기가 없는 경우
    return None

# 물고기 이동
def move_fish(array, now_x, now_y): # 배열, 상어좌표 x, y
    position = []
    for i in range(1,17):
        position = find_fish(array,i) # 물고기 좌표 찾기
        if position is None: # 빈칸인 경우
            continue
        x,y = position[0], position[1]
        dir = array[x][y][1] # 물고기 방향
        for i in range(8):
            nx,ny = x+dx[dir], y+dy[dir]
            if 0 <= nx < 4 and 0<=ny<4: # 경계값 안에 있는 경우
                if not (nx == now_x and ny == now_y): # 상어가 있는 칸이 아닌 경우
                    # 물고기 이동(값 변경)
                    array[x][y][0], array[nx][ny][0] = array[nx][ny][0], array[x][y][0]
                    array[x][y][1], array[nx][ny][1] = array[nx][ny][1], dir
                    break
            # 상어가 있거나, 경계값에 있다면 45도 반시계 방향으로 돌리기
            dir = (dir +1)%8


def dfs(array, x, y, total):
    global answer
    array = copy.deepcopy(array)

    # 해당 위치 물고기 먹기
    number = array[x][y][0]
    array[x][y][0] = -1

    # 물고기 이동
    move_fish(array, x, y)

    # 상어 이동할 수 있는 후보 확인
    result = shark_move(array, x, y)

    # 해당 먹이 먹는 모든 과정 탐색
    answer = max(answer, total + number)
    for next_x, next_y in result:
        dfs(array, next_x, next_y, total + number)


temp = [list(map(int, input().split())) for _ in range(4)]
array = [[None]*4 for _ in range(4)]
for i in range(4):
    for j in range(4):
        # array(x좌표, y좌표) : [물고기 번호, 방향 번호(0~7로변경)]
        array[i][j] = [temp[i][j*2], temp[i][j*2+1]-1]
# print(array)
answer = 0
dfs(array, 0, 0, 0) #(0,0) 상어 출발
print(answer)
반응형

유니온파인드( Union Find )

  • 한 그래프와 다른 한 그래프의 노드를 비교하여 서로 다른 그래프에 속해있다면 합쳐준다. 같은 그래프에 속해있는지 판단하는 방법은 서로의 루트노드가 같은지 비교하면 된다.

 

문제 모음

2021.06.18 - [알고리즘/baekjoon] - [백준/파이썬] 친구 네트워크, 수 찾기, SHA-356

 

[백준/파이썬] 친구 네트워크, 수 찾기, SHA-356

유형별 문제 풀이 - 고급 자료구조 - 03. 핵심 유형 문제풀이 # 친구 네트워크 (골드2) -> 문제유형 : 그래프, 해시, 집합, 유니온 파인드 import sys input = sys.stdin.readline def FindRoot(x): if x == pare..

jungeun960.tistory.com


프로그래머스 - 네트워크 (LEVEL 3)

def solution(n, computers):
    parents = [i for i in range(n)] # 초기값 각자 자기 자신을 부모로 가지고 있다

    def FindRoot(node): # 부모 노드를 찾는다
        parentNode = parents[node]
        # node의 parentnode가 자기 자신이 아닐경우 계속해서 상위노드를 탐색한다.
        if parentNode != node: 
            return FindRoot(parentNode)
        else:
            return parentNode

    def UnionSet(node1, node2): #두 노드의 부모를 병합(Union)한다
        rootNode1 = FindRoot(node1)
        rootNode2 = FindRoot(node2)
        if rootNode1 == rootNode2:
            # print(parents)
            return
        else:
            parents[rootNode1] = rootNode2
            # print(parents)
            return

    def CountRoot(parents): # 루트 노드의 갯수를 센다(덩어리 갯수 세기)
        roots = set()
        for i in range(n):
            roots.add(FindRoot(i))
        # print(roots)
        return len(set(roots))

    for node1 in range(n): # 입력값 computers에서 연결된 그래프를 찾는다
        for node2 in range(node1,n):
            if computers[node1][node2]:
                UnionSet(node1, node2)

    return CountRoot(parents)

 

+ dfs/bfs풀이 

더보기
def solution(n, computers):
	answer = 0 # 네트워크의 개수 저장
    q = [] # 탐색을 위한 큐
    visited = [0]*n # 방문한 노드 체크

    while 0 in visited: #모든값이 방문표시가 될때까지 반봇
        x = visited.index(0)
        q.append(x) # 첫 노드 인덱스 추가
        visited[x] = 1 # 첫 노드 방문 표시
        
        while q: 
            node = q.pop(0) # 큐 앞에서부터 노드 꺼내기
            visited[node] = 1
            for i in range(n): #인접 노드 방문
                if visited[i] == 0 and computers[node][i] == 1:
                    # 인접노드이고 방문한적 없는 경우
                    q.append(i) # 큐에 추가
                    visited[i] = 1 #방문했음을 표시
        answer += 1 # 한 네트워크 탐색을 마치면 개수 증가
    return answer

 


프로그래머스 - 섬 연결하기 (LEVEL 3)

  • 크루스칼 알고리즘(Kruskal Algorithm) : 가장 적은 비용으로 모든 노드를 연결하기 위해 사용하는 알고리즘으로 최소 비용 신장 트리(MST)를 만들기 위한 대표적인 알고리즘
  • 크루스칼 알고리즘(Kruskal Algorithm) = 유니온 파인드(Union Find) + 정렬
  • 최소 가중치를 기준으로 정렬한 후 순차적으로 접근하며 두 개의 노드가 연결되어 있지 않다면 연결하고 그 간선의 가중치를 더해준다. 그리고 두 개의 노드를 병합해준다

def solution(n, costs):
    answer = 0
    costs.sort(key=lambda x: x[2]) # 비용기준 오름차순 정렬
    parents = [i for i in range(n)]  # union-find 리스트 선언.
    
    def FindRoot(node): # 부모 노드를 찾는다
        parentNode = parents[node]
        if parentNode != node: 
            return FindRoot(parentNode)
        else:
            return parentNode

    def UnionSet(node1, node2): #두 노드의 부모를 병합(Union)한다
        rootNode1 = FindRoot(node1)
        rootNode2 = FindRoot(node2)
        if rootNode1 == rootNode2:
            return
        else:
            parents[rootNode1] = rootNode2
            return
    
    def findParent(node1, node2): # 두 노드가 연결되어 있는지 판별
        rootNode1 = FindRoot(node1)
        rootNode2 = FindRoot(node2)
        if rootNode1 == rootNode2:
            return 1
        else:
            return 0
    
    for s, d, cost in costs:
        # 두 노드가 연결되어 있지 않다면 연결시켜준다
        if findParent(s,d) != 1: 
            answer+= cost
            UnionSet(s,d)

    return answer
반응형

유형별 문제 풀이 - 기본 자료구조 - 02. 핵심 유형 문제풀이

# 1874 스택 수열 (실버3)

-> 문제 유형 : 스택, 그리디

스택 (stack)은 기본적인 자료구조 중 하나로, 컴퓨터 프로그램을 작성할 때 자주 이용되는 개념이다. 스택은 자료를 넣는 (push) 입구와 자료를 뽑는 (pop) 입구가 같아 제일 나중에 들어간 자료가 제일 먼저 나오는 (LIFO, Last in First out) 특성을 가지고 있다.

1부터 n까지의 수를 스택에 넣었다가 뽑아 늘어놓음으로써, 하나의 수열을 만들 수 있다. 이때, 스택에 push하는 순서는 반드시 오름차순을 지키도록 한다고 하자. 임의의 수열이 주어졌을 때 스택을 이용해 그 수열을 만들 수 있는지 없는지, 있다면 어떤 순서로 push와 pop 연산을 수행해야 하는지를 알아낼 수 있다. 이를 계산하는 프로그램을 작성하라.

ex)
n = 8
data = 4, 3, 6, 8, 7, 5, 2, 1

result = + + + + - - + + - + + - - - - -
stack  = 1 2 3 4  4 3 5 6 6 7 8 8 7 5 2 1 
n = int(input())

count = 1
stack = []
result = []

for i in range(n): # 데이터 개수만큼 반복
    data = int(input())
    while count <= data: # 입력받은 데이터에 도달할 때까지 삽입
        stack.append(count)
        result.append('+')
        count+=1
    if stack[-1] == data: # 스택의 최상위 원소가 데이터와 같을 때 
        stack.pop()
        result.append('-')
    else: # 불가능한 경우
        print('NO')
        exit(0)
        
print('\n'.join(result)) # 가능한 경우

 

# 1966 프린터 큐 (실버 3)

-> 문제 유형 : 큐, 구현, 그리디

여러분도 알다시피 여러분의 프린터 기기는 여러분이 인쇄하고자 하는 문서를 인쇄 명령을 받은 ‘순서대로’, 즉 먼저 요청된 것을 먼저 인쇄한다. 여러 개의 문서가 쌓인다면 Queue 자료구조에 쌓여서 FIFO - First In First Out - 에 따라 인쇄가 되게 된다. 하지만 상근이는 새로운 프린터기 내부 소프트웨어를 개발하였는데, 이 프린터기는 다음과 같은 조건에 따라 인쇄를 하게 된다.

  1. 현재 Queue의 가장 앞에 있는 문서의 ‘중요도’를 확인한다.
  2. 나머지 문서들 중 현재 문서보다 중요도가 높은 문서가 하나라도 있다면, 이 문서를 인쇄하지 않고 Queue의 가장 뒤에 재배치 한다. 그렇지 않다면 바로 인쇄를 한다.

예를 들어 Queue에 4개의 문서(A B C D)가 있고, 중요도가 2 1 4 3 라면 C를 인쇄하고, 다음으로 D를 인쇄하고 A, B를 인쇄하게 된다.

여러분이 할 일은, 현재 Queue에 있는 문서의 수와 중요도가 주어졌을 때, 어떤 한 문서가 몇 번째로 인쇄되는지 알아내는 것이다. 예를 들어 위의 예에서 C문서는 1번째로, A문서는 3번째로 인쇄되게 된다.

tc = int(input()) # 테스트케이스 개수

for i in range(tc):
    n,m = list(map(int, input().split())) # n : 문서의 개수, m : 확인하고 싶은 문서
    queue = []
    for index, i in enumerate(list(map(int, input().split()))):
        queue.append((index, i)) # (문서의 index, 중요도) 저장
    count = 0 # 인쇄한 문서 갯수

    while True:
        if queue[0][1] == max(queue, key=lambda x :x[1])[1] : # 맨앞의 문서가 현재 리스트중 중요도가 가장 높다면
            count += 1
            if queue[0][0] == m: # 그것이 확인하고 싶은 문서라면 출력
                print(count)
                break
            else: # 아니라면 갯수만 추가하고 pop 해준다
                queue.pop(0)
        else:   # 맨앞의 문서가 현재 리스트중 중요도가 높지 않다면 뒤로 보낸다
            queue.append(queue.pop(0))

 

# 5397 키로거 (실버 3)

-> 문제 유형 : 스택, 구현, 그리디

Point. 스택 2개를 만들어 스택 두개의 중간 지점을 커서로 간주한다.

창영이는 강산이의 비밀번호를 훔치기 위해서 강산이가 사용하는 컴퓨터에 키로거를 설치했다. 며칠을 기다린 끝에 창영이는 강산이가 비밀번호 창에 입력하는 글자를 얻어냈다.

키로거는 사용자가 키보드를 누른 명령을 모두 기록한다. 따라서, 강산이가 비밀번호를 입력할 때, 화살표나 백스페이스를 입력해도 정확한 비밀번호를 알아낼 수 있다.

강산이가 비밀번호 창에서 입력한 키가 주어졌을 때, 강산이의 비밀번호를 알아내는 프로그램을 작성하시오.

ex) <<BP<A>>Cd-

data left_stack 커서 right_stack
<      
<      
B B    
P BP    
< B   P
A BA   P
> BAP    
>      
C BAPC    
d BAPCD    
- BAPC    

 

tc = int(input()) # 테스트케이스 개수

for i in range(tc):
    left_stack = [] # 스택 두개를 만들어, 스택 두개의 중간 지점을 커서로 간주합니다.
    right_stack = []
    data = input()
    for i in data:
        if i == '-': # 왼쪽 스택에서 원소를 삭제합니다
            if left_stack:
                left_stack.pop()
        elif i == '<': # 왼쪽 스택에서 오른쪽 스택으로 원소를 이동합니다.
            if left_stack:
                right_stack.append(left_stack.pop())
        elif i == '>': # 오른쪽스택에서 왼쪽 스택으로 왼소를 이동합니다.
            if right_stack:
                left_stack.append(right_stack.pop())
        else: # 왼쪽 스택에 원소를 삽입합니다.
            left_stack.append(i)
    left_stack.extend(reversed(right_stack)) # 오른쪽 스택을 뒤집어서 왼쪽 스택에 더해줍니다
    print(''.join(left_stack))
반응형

유형별 문제 풀이 - 기본 자료구조 - 01. 기초 문제풀이

# 2920 음계 (브론즈2)

-> 문제 유형 : 배열, 구현

다장조는 c d e f g a b C, 총 8개 음으로 이루어져있다. 이 문제에서 8개 음은 다음과 같이 숫자로 바꾸어 표현한다. c는 1로, d는 2로, ..., C를 8로 바꾼다.

1부터 8까지 차례대로 연주한다면 ascending, 8부터 1까지 차례대로 연주한다면 descending, 둘 다 아니라면 mixed 이다.

연주한 순서가 주어졌을 때, 이것이 ascending인지, descending인지, 아니면 mixed인지 판별하는 프로그램을 작성하시오.

a = list(map(int, input().split()))
ascending, descending = False, False
start = a[0]
for i in range(1,len(a)):
    if start > a[i]:
        descending = True
    elif start < a[i]:
        ascending = True
    start = a[i]

if ascending == True and descending == False:
    print('ascending')
elif ascending == False and descending == True:
    print('descending')
else:
    print('mixed')

 

# 2798 블랙잭 (브론즈2)

-> 문제유형 : 배열, 조합, 완전탐색

카지노에서 제일 인기 있는 게임 블랙잭의 규칙은 상당히 쉽다. 카드의 합이 21을 넘지 않는 한도 내에서, 카드의 합을 최대한 크게 만드는 게임이다. 블랙잭은 카지노마다 다양한 규정이 있다.

한국 최고의 블랙잭 고수 김정인은 새로운 블랙잭 규칙을 만들어 상근, 창영이와 게임하려고 한다.

김정인 버전의 블랙잭에서 각 카드에는 양의 정수가 쓰여 있다. 그 다음, 딜러는 N장의 카드를 모두 숫자가 보이도록 바닥에 놓는다. 그런 후에 딜러는 숫자 M을 크게 외친다.

이제 플레이어는 제한된 시간 안에 N장의 카드 중에서 3장의 카드를 골라야 한다. 블랙잭 변형 게임이기 때문에, 플레이어가 고른 카드의 합은 M을 넘지 않으면서 M과 최대한 가깝게 만들어야 한다.

N장의 카드에 써져 있는 숫자가 주어졌을 때, M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 구해 출력하시오.

import itertools

N, M = list(map(int, input().split())) # N : 카드의 개수, M : 최대 수
card = list(map(int, input().split())) # 카드에 쓰여진 수
answer = 0

nCr = list(itertools.combinations(card, 3)) # 3가지 조합 찾아내기
nCr_sum = []
for i in nCr: # 고른 카드의 합 구하기
    nCr_sum.append(sum(i))
nCr_sum.sort() # 정렬하기

for i in nCr_sum: # M을 넘지않으면서 가까운 카드 찾아내기
    if i <= M:
        answer = i
    else:
        break
print(answer)

 

+ 동빈나 풀이

n,m = list(map(int, input().split(' ')))
data = list(map(int, input().split(' ')))

result =0
length = len(data)
sum_value = 0
for i in range(0, length):
    for j in range(i+1, length):
        for k in range(j+1, length):
            sum_value = data[i]+data[j]+data[k]
            if sum_value <= m:
                result = max(result, sum_value)

print(result)
반응형

'알고리즘 > baekjoon' 카테고리의 다른 글

[ 백준 / 파이썬 ] 19236 청소년 상어  (0) 2021.06.08
[ 백준 / 파이썬 ] 스택 수열, 프린터 큐, 키로거  (0) 2021.05.27
20210223_코테공부  (0) 2021.02.23
20210210_코테공부  (0) 2021.02.10
20210209_코테공부  (0) 2021.02.09

+ Recent posts