Skip to content

Minjeong / 7월 4주차 / 8문제#47

Merged
Mingguriguri merged 8 commits intomainfrom
minjeong
Jul 29, 2024
Merged

Minjeong / 7월 4주차 / 8문제#47
Mingguriguri merged 8 commits intomainfrom
minjeong

Conversation

@Mingguriguri
Copy link
Collaborator

푼 문제가 여러 개라면 아래 폼 형식 복사해서 넣어주시면 됩니다 :D

주 목표 문제 수: 3개

푼 문제 8문제


지난 주 발제 문제: 백준 #18352. 특정 거리의 도시 찾기: 그래프 / 실버2

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. 도시의 개수 N, 도로의 개수 M, 최단거리 K, 출발도시 번호 X를 입력받고, 방문여부를 판단하는 visited 리스트와 그래프로 표현할 graph 리스트, 정답을 저장할 answer 리스트를 초기화한다. 이때 visited 리스트는 방문 여부를 판단할 뿐만 아니라 거리를 저장하는 역할로써 사용한다.
  2. 그래프를 단방향으로 연결한다.
  3. BFS 함수를 정의한다.
    1. queue를 deque 자료형으로 선언한다.
    2. queue가 있는 동안 아래 과정을 반복한다.
    3. 첫번째 노드를 pop하고, 해당 노드와 연결되어 있으며, 방문하지 않고, 첫번째로 시작한 노드가 아닌 연결된 노드라면, queue에 추가하고, 방문 표시를 한다. 이때 방문 표시는 현재 노드의 거리에 +1을 해준다.
    4. 동시에 만약 연결된 노드의 거리(visited[connected_node])가 최단거리 k와 같다면 answer 리스트에 추가한다.
  4. BFS 함수를 호출하여 answer 리스트를 반환받는다.
  5. answer리스트가 존재하지 않는 경우는 최단 거리 k인 도시가 하나도 존재하지 않는 경우이기 때문에 -1을 출력하고,
    존재하는 경우에는 오름차순으로 정렬한 후 한 줄에 하나씩 출력한다.

🚩제출한 코드

import sys
from collections import deque

input = sys.stdin.readline

def bfs(start):
    global k
    queue = deque([start])
    visited[start] = 0 # 시작 노드의 거리를 0으로 설정
    while queue:
        node = queue.popleft()
        for connected_node in graph[node]:                  # 해당 노드와 연결된 노드 순회
            if visited[connected_node] == -1:               # 방문하지 않은 노드일 경우
                queue.append(connected_node)                # 큐에 추가하고
                visited[connected_node] = visited[node] + 1 # 거리를 1 증가

                # 연결된 노드의 거리가 k와 같다면 answer 리스트에 추가
                if visited[connected_node] == k:
                    answer.append(connected_node)
    return answer

# 도시 개수 n, 도로 개수 m, 거리 정보 k, 출발도시 번호 x
n, m, k, x = map(int, input().strip().split())
visited = [-1 for _ in range(n + 1)] # 모든 노드를 -1로 초기화 (방문하지 않은 상태)
graph = [[] for _ in range(n + 1)]
answer = []

# 그래프 연결
for _ in range(m):
    u, v = map(int, input().strip().split())
    graph[u].append(v)

# BFS 호출 -> 출발도시 번호 x로 시작
answer = bfs(x)
if not answer:
    print(-1)
else:
    answer.sort()
    for u in answer:
        print(u)

💡TIL

배운 점이 있다면 입력해주세요

  • 시간 제한이 있는 문제라 for문을 적절하게 사용하는 것이 중요한 문제였다.
  • 예시 답변이 모두 정답임에도 불구하고 “틀렸습니다”가 계속 반복해서 나왔다. 그 이유는 시작하는 노드를 다시 재방문하게 되는 경우를 고려하지 않았고, 시작하는 노드의 방문 표시를 하지 않았기 때문이다. 시작하는 노드도 방문 표시해줘야 한다는 점 잊지 말자!

백준 #13305. 주유소: 그리디 / 실버3

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. 도시의 개수를 n, 도로의 길이를 road 리스트, 각 도시의 리터당 가격을 oil_price 리스트에 저장을 하고, 최저 비용인 min_cost는 0으로, 도시의 기름 가격을 나타내는 min_price는 첫 번째 도시의 기름 가격으로 초기화한다.
  2. 각 도시를 순회하며, 다음 도시로 이동하는데 필요한 기름 비용을 계산한다,
    1. 현재 도시에서의 기름 가격과 최소 가격을 비교하여 더 작은 값을 최소 가격 min_price로 갱신한다.
    2. 최저 비용 min_cost에 최소 가격과 현재 도로의 길이를 곱한 값을 누적하여 계산한다.
  3. 최종 결과인 min_cost를 출력한다.

🚩제출한 코드

import sys
input = sys.stdin.readline

# 초기화
n = int(input()) # n: 도시의 개수
road = list(map(int, input().split())) # 도로의 길이
oil_price = list(map(int, input().split())) # 리터당 가격

min_cost = 0 # 최저 비용
min_price = oil_price[0] # 첫번째 도시의 기름 가격

for i in range(n - 1):
    if oil_price[i] < min_price:  # 현재 도시에서의 기름 가격이 최소 가격보다 작으면 갱신
        min_price = oil_price[i]
    min_cost += min_price * road[i] # 현재 도시에서 다음 도시로 이동할 때 드는 비용을 추가

print(min_cost)

💡TIL

배운 점이 있다면 입력해주세요

  • 그리디 알고리즘은 현재 상황에서 최적인 선택을 하는 방법! 이 문제에서는 가장 저렴한 주유소를 이용해 주유하는 것이 핵심이다.

백준 #9148. 신나는 함수 실행: DP / 실버2

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. (a, b, c)로 접근해야 하므로 리스트가 아니라 딕셔너리 memo를 초기화한다.
  2. 코드가 실행될 때마다 memo 딕셔너리에 값이 저장되도록 하고, 마지막에 memo[(a, b, c)]의 값을 반환하도록 설정한다.
  3. -1 -1 -1이 입력될 때까지 무한 반복하여 a, b, c를 입력받는다. 문제에서 설정한 출력형식에 맞게 w 함수의 결과를 반환한다.

🚩제출한 코드

import sys
input = sys.stdin.readline

def w(a, b, c):
    if (a, b, c) in memo:
        return memo[(a, b, c)]

    if a <= 0 or b <= 0 or c <= 0:
        memo[(a, b, c)] = 1
        return 1

    elif a > 20 or b > 20 or c > 20:
        memo[(a, b, c)] = w(20, 20, 20)

    elif a < b and b < c:
        memo[(a, b, c)] = w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c)

    else:
        memo[(a, b, c)] = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1)
    
    return memo[(a, b, c)]

memo = {}
while True:
    a, b, c = map(int, input().split())
    if a == -1 and b == -1 and c == -1:
        break
    print('w(%d, %d, %d) = %d'%(a, b, c, w(a, b, c)))

💡TIL

배운 점이 있다면 입력해주세요

  • DP는 무조건 리스트로 구현해야 한다고 생각했는데 딕셔너리로도 메모이제이션이 가능하다는 것을 알게 되었다.
  • 재귀호출해서 오래 걸리는 이유는 계속해서 같은 값을 연산하는 것을 재귀적으로 반복하기 때문이다. 이럴 때 연산의 결과를 DP에 저장하면 된다ㅣ.

백준 #14940. 쉬운 최단거리: 그래프 / 실버1

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. 세로 길이 n, 가로 길이 m, 지도 정보를 grid 리스트로 입력받는다.
  2. 각 위치마다 목표지점까지 거리를 구하는 함수 find_distance_to_goal를 호출한다.
    1. 거리배열 distances을 -1로 초기화한다. (거리 정보가 없는 경우 -1로 해야 하기 때문에)
    2. grid를 순회하며 목표지점인 2와 땅이 아닌 지점 0을 찾아내어 거리 배열distances 에 해당하는 위치의 값을 0으로 설정한다.
    3. 목표지점(target_x, target_y)의 위치를 큐 queue에 첫번째 값으로 초기화한다.
    4. 상하좌우 방향 리스트 directions도 설정한다.
    5. queue에 값이 있는 동안 현재 지점을 꺼내와 상하좌우 인접한 지점이 지도 범위 안에 있고, 갈 수 있는 땅(1)이고, 아직 방문하지 않았을 경우에 인접한 지점의 거리를 현재 거리+1로 설정하고 큐에 추가한다.
    6. queue에 값이 없어질동안 e 과정을 반복하여 BFS 탐색한다
    7. 탐색이 끝나면 거리배열 distances를 반환한다.
  3. distances를 한 줄에 공백을 두어 출력한다.

🚩제출한 코드

import sys
from collections import deque
input = sys.stdin.readline

# 목표지점까지의 거리 구하기
def find_distance_to_goal(n, m, grid):
    # 거리배열(모든 지점에 대한 거리를 저장)
    distances = [[-1 for _ in range(m)] for _ in range(n)]

    # 목표지점 찾기
    for i in range(n):
        for j in range(m):
            if grid[i][j] == 2:     # 목표지점의 위치 저장하고, 0으로 설정
                target_x = i
                target_y = j
                distances[i][j] = 0
            if grid[i][j] == 0:     # 땅이 아닌 곳이므로 0으로 설정
                distances[i][j] = 0

    queue = deque([(target_x, target_y)])           # 큐 초기화
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 방향 리스트

    # BFS 탐색
    while queue:
        x, y = queue.popleft()      # 현재지점 pop
        for dx, dy in directions:
            nx, ny = x + dx, y + dy # 현재 지점에서 상하좌우 인접한 지점
            # 인접한 지점이 지도의 범위 안에 있고, 갈 수 있는 땅(값이 1)이며, 아직 방문하지 않은 지점이라면:
            if (0 <= nx < n and 0 <= ny < m) and grid[nx][ny] == 1 and distances[nx][ny] == -1:
                    distances[nx][ny] = distances[x][y] + 1 # 인접한 지점의 거리를 현재 지점의 거리 + 1로 설정
                    queue.append((nx, ny))                  # 인접한 지점을 큐에 추가

    return distances

# 초기화
n, m = map(int, input().split())    # n: 세로, m: 가로
grid = []                           # 지도
for _ in range(n):
    line = list(map(int, input().split()))
    grid.append(line)

# 함수 호출
distances = find_distance_to_goal(n, m, grid)

# 정답 출력
for d in distances:
    print(" ".join(map(str, d))) # 공백을 구분하여 출력

💡TIL

배운 점이 있다면 입력해주세요

  • BFS로 탐색할 때는 상하좌우 방향 리스트 directions도 필요하다.

      directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 방향 리스트
  • 문제를 너무 어렵게 생각했던 것 같다. 이럴 때에는 갈 수 없는 부분, 갈 수 있는 부분을 ‘조건문’을 두어 구분한다.

  • 큐에 무엇을 넣어야 할 지 고민해보자.

  • 공백을 구분하여 출력하려면, 2중 for문을 사용해도 되지만 아래와 같이 코드를 작성하면 된다.

    for d in distances:
        print(" ".join(map(str, d))) # 공백을 구분하여 출력
  • 이 문제가 대표적인 BFS 문제라고 한다. 이런 유형을 자주 풀어서 BFS도 정복해야겠다!


백준 #30802. 웰컴키트: 수학 / 브론즈3

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  • 티셔츠를 T장씩 최소 몇 묶음 주문해야 하는지:
    • 각 티셔츠사이즈별 신청자 수를 T 로 나눈 몫에 올림처리하면 된다.
  • 펜을 P 자루씩 최대 몇 묶음 주문할 수 있고, 그 때 펜을 한 자루씩 몇 개 주문하는지:
    • 참가자 수 NP로 나눈 몫(N//P)이 최대 주문할 수 있는 묶음 수이고,
    • 참가자 수 NP로 나눈 나머지(N%P)가 한 자루씩 주문해야 하는 수이다.

🚩제출한 코드

import sys
from math import ceil

input = sys.stdin.readline

n = int(input()) # 참가자 수
sizes = list(map(int, input().split())) # 티셔츠사이즈별 신청자 수(S, M, L, XL, XXL, XXXL)
t, p = map(int, input().split()) # t: 티셔츠 묶음 수, p: 펜 묶음 수

min_t_shirts = 0
for s in sizes:
    min_t_shirts += ceil(s / t)

print(min_t_shirts) # t장씩 최소 몇 묶음 주문해야 하는지
print(n // p, n % p)# p자루씩 최대 몇 묶음 주문할 수 있는지와, 그 때 펜을 한 자루씩 몇 개 주문하는지

💡TIL

배운 점이 있다면 입력해주세요

  • 반올림 처리 함수: ceil(), floor(), round()
    • ceil(): 소수점 자리에서 무조건 올림
    • floor(): 소수점 아래 다 무시
    • round(): 반올림 함수

백준 #28702. FizzBuzz: 수학, 문자열 / 브론즈1

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. FizzBuzz 문자열 배열 fizzbuzz을 초기화하고 입력받는다
  2. fizzbuzz에 숫자가 있는지 파악한다. 숫자가 있다면, 해당 문자열의 위치를 통해 정답을 구하고 answer 변수에 저장한다.
  3. FizzBuzz 규칙에 따라 3 또는 5의 배수인 경우 해당하는 문자를 출력하고 해당하지 않을 경우 answer 그대로 출력한다.

🚩제출한 코드

import sys
from math import ceil
input = sys.stdin.readline

# FizzBuzz 문자열 배열
fizzbuzz = [0] * 3
for i in range(3):
    fizzbuzz[i] = input().strip()

for s in fizzbuzz:
    if s.isdigit(): # 문자열이 숫자로 이루어져있다면,
        idx = fizzbuzz.index(s) # 해당 문자열의 위치를 저장하여
        answer = int(fizzbuzz[idx]) + (3 - idx) # 정답 값을 구하기

# FizzBuzz 규칙에 따라 정답 출력
if answer % 3 == 0 and answer % 5 == 0:
    print("FizzBuzz")
elif answer % 3 == 0:
    print("Fizz")
elif answer % 5 == 0:
    print("Buzz")
else:
    print(answer)

💡TIL

배운 점이 있다면 입력해주세요

다른 분의 코드를 보고 배웠다.
출처

for i in range(3, 0, -1):
    x = input()
    if x not in ['Fizz', 'Buzz', 'FizzBuzz']:
        n = int(x) + i
        break
print('Fizz'*(n % 3 == 0) + 'Buzz'*(n % 5 == 0) or n)

어떻게 코드를 간단하게 할 수 있는지 감탄하였다.

  1. Fizz, Buzz, FizzBuzz가 아닌 경우는 숫자이므로 이를 정수형으로 형변환하고, i를 더해준다. (이때 i는 3에서 1까지 -1씩 줄어든다.)
  2. print('Fizz'*(n % 3 == 0) + 'Buzz'*(n % 5 == 0) or n)
    • n % 3 == 0이 True인 경우 Fizz가 출력된다. False인 경우 아무것도 출력이 안 된다.
    • n % 5 == 0이 True인 겨우 Buzz가 출력된다. False인 경우 아무것도 출력이 안 된다.
    • 따라서 둘 다 True인 경우 FizzBuzz가 출력된다.
    • 둘 다 해당하지 않을 경우 n이 출력된다.

백준 #31403. A+B-C: 수학 / 브론즈4

(안 풀어도 되지만 solved.ac Class 1을 마무리하려고 풀었음)

🚩제출한 코드

import sys
from math import ceil
input = sys.stdin.readline

a = int(input())
b = int(input())
c = int(input())

print(a + b - c)
print(int(str(a)+str(b))-c)

백준 #18405. 경쟁적 전염: 그래프 / 골드5

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. 시험관 크기 n, 바이러스의 개수 k, 시험관 정보 grid 리스트, 결과를 확인하고자 하는 초 s, 위치 x, y를 입력받는다.
  2. grid리스트를 순회하며 바이러스의 정보를 바이러스 정보 리스트인 virus_info에 저장한다.(바이러스의 번호, 시간, x좌표, y좌표)
  3. 문제에서 매 초바다 번호가 낮은 종류의 바이러스부터 먼저 증식한다고 하였으므로 virus_info를 오름차순으로 정렬한다.
  4. queuevirus_info를 넣어 초기화한다.
  5. BFS 탐색을 수행한다.
    1. queue가 빌 때까지 아래 과정을 반복한다.
    2. queue에서 바이러스 정보를 꺼내 바이러스 번호 virus_num, 현재 시간 time, x좌표 x, y좌표 y에 저장한다.
    3. 이때 현재 시간(time)이 목표한 시간(s)과 같다면 반복을 종료한다.
    4. 그게 아니라면, 상하좌우 네 방향으로 인접한 범위를 탐색한다.
    5. 새로운 위치 nx, ny를 계산하여 새로운 위치가 시험관 범위 안에 있고 아직 바이러스가 없는 경우 현재의 바이러스(virus_num)를 전파한다. 이후 해당 바이러스의 정보를 큐queue에 추가한다.
  6. 탐색한 후, 목표 위치의 바이러스의 번호를 반환하여 출력한다.

🚩제출한 코드

import sys
from collections import deque
input = sys.stdin.readline

def get_result(goal_sec, goal_x, goal_y, grid, n, k):
    virus_info = [] # 바이러스 정보를 담는 리스트 초기화

    # 시험관을 순회하며 바이러스 팀섹
    for i in range(n):
        for j in range(n):
            if grid[i][j] != 0:                             # 바이러스라면
                virus_info.append((grid[i][j], 0, i, j))    # 바이러스 번호, 현재 시간, x좌표, y좌표를 추가

    virus_info.sort()                               # 바이러스 정보 리스트를 오름차순으로 정렬
    queue = deque(virus_info)                       # 큐 초기화
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 방향 리스트 (상, 하, 좌, 우)

    while queue:
        virus_num, time, x, y = queue.popleft()

        # 목표 시간에 도달하면 종료
        if time == goal_sec:
            break

        for dx, dy in directions:
            nx, ny = x + dx, y + dy # 현재 지점에서 상하좌우 인접한 지점
            #  시험관 범위 안에 있고 아직 바이러스가 없는 경우 현재의 바이러스를 전파
            if (0 <= nx < n and 0 <= ny < n) and grid[nx][ny] == 0:
                grid[nx][ny] = virus_num
                queue.append((virus_num, time + 1, nx, ny))

    return grid[goal_x][goal_y]

# 입력 처리
n, k = map(int, input().split())    # n: 시험관 크기 / k: 바이러스 개수
grid = []                           # 시험관 정보
for _ in range(n):
    li = list(map(int, input().split()))
    grid.append(li)
s, x, y = map(int, input().split()) # 시간, x좌표, y좌표

# 함수 호출 및 결과 출력
print(get_result(s, x-1, y-1, grid, n, k))

💡TIL

배운 점이 있다면 입력해주세요

@Mingguriguri Mingguriguri self-assigned this Jul 29, 2024
@Mingguriguri Mingguriguri merged commit 3ccaa65 into main Jul 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant