Skip to content

Conversation

@Mingguriguri
Copy link
Collaborator

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

🌱WIL

이번 한 주의 소감을 작성해주세요!

  • 이번에는 최대한 문제 유형을 골고루 풀어보려고 해보았다. 그래서 그런지 골고루 헷갈렸다..ㅋㅋㅋㅋ
    • DP나 그리디 문제는 순전히 아이디어의 싸움이었는데 한번 아이디어가 안 떠오르면 계속 안 떠오르는 것 같다. 그래서 나름대로 예외경우도 생각해보고, 문제를 제대로 이해하는 것이 중요한 것 같다.
    • BFS 유형은 난이도가 있는 것이다보니까 새로운 접근법을 알게 되는 문제들이었다. [벽 부수고 이동하기] 문제의 경우에는 visited 리스트를 3차원으로 관리하는 것이 신선했다. wall부분을 나누어 2개의 최단경로로 관리했다. [구슬탈출] 문제는 visited 리스트를 처음에 초기화해놓지 않고 방문할 때마다 방문한 구슬의 위치를 추가하는 식으로 상태를 관리했다. 방문 로직 처리를 이렇게 처리하는 방식이 신선하고 앞으로 풀 때 참고해야겠다고 생각이 들었다.
  • 문제를 풀 때 내 치명적인 실수가 무엇인지 알게 되었다. 나는 문제를 너무 대충 읽는다!! 그래서 0이나 O와 같은 문자를 착각하기도 하고, 문제를 제대로 이해 안 하고 접근하기도 했다. 이런 부분은 반성하고 문제를 온전히 이해하는데에 시간을 더 써야 할 것 같다.

🚀주간 목표 문제 수: 5개

푼 문제


백준 #2206. 벽 부수고 이동하기 : BFS / 골드3

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

🚩플로우 (선택)

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

  1. 맵 크기와 맵 정보를 입력받는다. 행: N, 열: M, 각 행의 정보: maps

    maps 리스트에서 0은 이동 가능한 곳, 1은 벽이다.

  2. 방문 배열을 초기화한다.

    • visited[x][y][0]: 벽을 뚫지 않고 도달했을 때의 최단 거리.
    • visited[x][y][1]: 벽을 한 번 뚫고 도달했을 때의 최단 거리.
    • (0, 0, 0) 시작 위치에서 초기 값은 1로 설정한다
  3. BFS 큐를 초기화하고 탐색 방향을 설정한다.

    • queue = deque([(0, 0, 0)]): BFS 시작 위치(벽 미뚫음 상태).
    • directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]: 상우하좌 4방향 설정.
  4. BFS 탐색을 수행한다.

    1. 현재 위치 (x, y)와 벽 부순 상태 wall을 큐에서 꺼낸다.
    2. 만약 현재 위치가 (N-1, M-1)이라면, 최단 경로 visited[x][y][wall] 출력 후 종료한다.
    3. 상우하좌로 네 방향 탐색을 수행한다.
      1. 다음 위치를 계산한다. (nx, ny)
      2. nx, ny가 맵 범위를 벗어나면 무시한다.
      3. 이동 조건을 확인하여 처리한다.
        • 벽인 경우(maps[nx][ny] == 1)
          • 벽을 부순 적이 없는 경우(wall == 0):
            • 벽을 부수고 이동
            • visited[nx][ny][1] = visited[x][y][0] + 1
            • 큐에 queue.append((nx, ny, 1)) 추가한다.
        • 벽이 아닌 경우(maps[nx][ny] == 0)
          • 벽을 부수지 않은 경우(visited[nx][ny][wall] == 0):
            • 이동 및 방문 처리
            • visited[nx][ny][wall] = visited[x][y][wall] + 1
            • 큐에 queue.append((nx, ny, wall)) 추가.
  5. BFS 탐색이 종료될 때까지 목표 지점에 도달하지 못한 경우 -1 을 출력한다.

🚩제출한 코드

from collections import deque

# 입력 받기
N, M = map(int, input().split())
maps = []
for _ in range(N):
    temp = input().strip()
    maps.append(list(map(int, temp)))

# 방문 배열 초기화
visited = [[[0] * 2 for _ in range(M)] for _ in range(N)]
visited[0][0][0] = 1
# visited[x][y][0] = 벽을 뚫지 않고 온 최단 경로
# visited[x][y][1] = 벽을 1회 뚫고 온 최단 경로

# BFS 탐색 방향 (상, 우, 하, 좌)
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]

# BFS 큐 초기화
queue = deque([(0, 0, 0)])

# BFS 시작
while queue:
    x, y, wall = queue.popleft()
    
    # 목표 지점 도달 시 최단 거리 출력
    if x == N - 1 and y == M - 1:  # 목표지점에 도달했다면
        print(visited[x][y][wall])
        exit(0)

		# 네 방향 탐색
    for dx, dy in directions:
        nx, ny = x + dx, y + dy
	
				# 맵 범위를 벗어난 경우 무시
        if nx < 0 or nx >= N or ny < 0 or ny >= M:
            continue
            
        # 벽이고 벽 파괴를 아직 안 쓴 경우, 벽 부수기 가능. 값을 업데이트하고 큐에 추가
        if maps[nx][ny] == 1 and wall == 0:
            visited[nx][ny][1] = visited[x][y][0] + 1
            queue.append((nx, ny, 1))
        # 벽이 아니고, 아직 방문하지 않았을 경우에는 이동 가능하므로 큐에 추가
        elif maps[nx][ny] == 0 and visited[nx][ny][wall] == 0:
            visited[nx][ny][wall] = visited[x][y][wall] + 1
            queue.append((nx, ny, wall))
            
# 도달하지 못한 경우 -1 출력
print(-1)

💡TIL

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

  • visited 리스트를 3차원으로 관리하는 것이 신선했다.
  • 처음에는 wall 부분을 wall = 1 로 직접 변경했는데, BFS는 경로별로 독립적인 상태 관리가 필요하기 때문에 직접 변수로 대입하면 방문할 때 문제가 생긴다는 것을 알게 되었다. 이 부분은 앞으로 주의해야겠다.
  • 벽 부수기 상태를 나누어 관리해서 2개의 최단 경로를 따로 관리하는 것이 핵심인 문제였다.

백준 #11727. 2xn 타일링 2: DP / 실버3

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

🚩플로우 (선택)

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

  1. 입력값 n을 받는다.
  2. 배열 dp를 초기화한다.
  3. 초기값을 설정한다.
    1. 초기값 dp[1] = 1 설정한다.
    2. 만약 n >= 2일 경우 dp[2] = 3으로 설정한다.
  4. 점화식을 사용해 dp[i] = dp[i-1] + 2 * dp[i-2]를 계산한다. 이때 dp에 저장할 때 10007로 나눈 나머지값을 저장한다.
  5. dp[n]을 출력한다.

🚩제출한 코드

import sys
input = sys.stdin.readline

# 입력 받기
n = int(input())

# DP 배열 초기화
dp = [0] * (n+1)
dp[1] = 1

# 초기값 설정
if n >= 2:
    dp[2] = 3

# 점화식을 이용해 계산
for i in range(3, n+1):
    dp[i] = (dp[i-1] + 2 * dp[i-2]) % 10007

# 결과 출력
print(dp[n])

💡TIL

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

  • 점화식을 생각해내는 것이 어려웠다. 역시 DP는 점화식 아이디어만 잘 떠올리면 풀기 쉽지만 그 아이디어를 떠올리기까지는 쉽지 않을 것 같다.

백준 #1026. 보물: 그리디 / 실버4

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

🧠풀이 아이디어

  • 가장 작은 값 A[i]는 가장 큰 값 B[i]와 곱해야 한다.
  • 문제에서는 배열 B가 고정되어 있다고 했지만, 계산 시 정렬을 해도 결과는 동일하다.
  • 즉, 배열 A오름차순 정렬, 배열 B내림차순 정렬을 수행하면 항상 최소값을 만들 수 있다.

🚩플로우 (선택)

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

  1. N, 배열 A, 배열 B를 입력받는다.
  2. A는 오름차순으로 정렬하고 B는 내림차순으로 정렬한다.
  3. 배열 A[i] * B[i]를 모두 더한 값을 answer 변수에 저장한다.
  4. 결과값 answer를 출력한다.

🚩제출한 코드

import sys
input = sys.stdin.readline

# 입력 받기
N = int(input())  # 배열 길이
A = list(map(int, input().split()))  # 배열 A
B = list(map(int, input().split()))  # 배열 B

# A 오름차순 정렬, B 내림차순 정렬
A.sort()
B.sort(reverse=True)

# 최소값 계산
answer = 0
for i in range(N):
    answer += A[i] * B[i]

# 결과 출력
print(answer)

💡TIL

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

  • 문제는 그리디 알고리즘으로, 배열 AB가 서로 어떻게 정렬되어야 최소값이 나오는지를 판단하는 것이 핵심이었다.
  • pop() 을 사용한 다른 풀이 방식을 보고 흥미로웠다. 하지만 이 문제의 제한 시간 범위 안에서는 정렬로 풀어도 시간복잡도와 가독성 면에서 효율적인 것 같다.

백준 #2217. 로프 : 그리디 / 실버4

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

🧠 풀이 아이디어

  • 로프의 중량 제한이 적은 로프가 병렬로 연결될 때 병목이 된다.
  • 따라서 작은 값부터 차례대로 로프 개수를 늘려가며 계산해야 한다.
  • 각 로프의 최대 중량을 계산할 때:
    • 최대 중량 = 현재 로프의 최대 하중 × 현재 사용된 로프 수
  • 정렬과 그리디 사용
    • 가장 큰 값을 먼저 사용하는 게 유리하므로 내림차순 정렬한다.
    • 각 로프를 하나씩 더하면서, 해당 로프의 최대 중량 × 사용된 로프 수를 계산한다.
    • 계산된 값 중 가장 큰 값을 최대 중량으로 출력한다

🚩플로우 (선택)

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

  1. 로프 개수 N과 각 로프의 최대 중량을 입력받는다. 최대 중량은 rope 리스트에 저장한다.

  2. rope의 최대 중량을 내림차순으로 정렬한다.

  3. 첫 번째 로프부터 하나씩 추가하며, 현재 사용된 로프 수에 따라 최대 중량을 계산한다.

    현재 로프의 하중 × 사용된 로프 수

  4. 최대 중량 값을 업데이트하며, 최종 계싼된 max_weight를 출력한다.

🚩제출한 코드

import sys
input = sys.stdin.readline

# 입력 받기
N = int(input())  # 로프 개수
rope = [int(input()) for _ in range(N)]

# 내림차순 정렬
rope.sort(reverse=True)

# 최대 중량 계산
max_weight = 0
for i in range(N):
    # i + 1: 현재 사용된 로프 개수
    max_weight = max(max_weight, rope[i] * (i + 1))

print(max_weight)

💡TIL

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

  • 처음에는 문제를 잘못 이해해서 아예 다른 방향으로 문제에 접근해서 풀이했다. 예제도 1개밖에 없어서 문제를 오롯이 이해하는 것에 시간이 걸렸다.
  • 솔직히 아직도 문제만 보면 완전하게 말하고자 하는 게 잘 이해가 안 된다. 이건 독해력을 좀 길러야 할 것 같다… ^^;
  • 문제를 풀면서 그리디에서도 규칙을 파악해야 한다는 것을 다시 깨닫게 되었다.

백준 #13460. 구슬 탈출 2: BFS / 골드1

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

🚩플로우 (선택)

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

  1. 보드 크기와 정보 입력
    • N, M을 입력받아 보드의 세로와 가로 크기를 설정한다.
    • board 리스트에 각 행의 보드 상태를 입력받는다.
    • 보드 정보는 '.', '#', 'O', 'R', 'B'로 이루어진다.
    • 빨간 구슬 R과 파란 구슬 B의 초기 위치를 찾는다.
  2. 방문 상태 초기화
    • visited(rx, ry, bx, by) 형태로 저장해 빨간 구슬과 파란 구슬의 상태를 관리한다.
    • set을 사용해 중복 상태를 방지하고, 처음 위치 (rx, ry, bx, by)를 큐에 넣고 방문 처리한다.
  3. BFS 큐 초기화 및 탐색 방향 설정
    • queue = deque([(rx, ry, bx, by, 1)])
      • (rx, ry, bx, by, 1): 빨간 구슬과 파란 구슬의 초기 위치와 이동 횟수(1부터 시작).
    • directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
      • 상, 우, 하, 좌 네 방향으로 구슬을 이동한다.
  4. BFS 탐색 진행
    1. 큐에서 (rx, ry, bx, by, depth)를 꺼낸다.
    2. 이동 횟수 depth가 10을 초과하면 실패로 간주하고 1을 출력한 후 종료한다.
    3. 4방향(상, 우, 하, 좌)으로 구슬을 기울여 탐색한다.
      1. 구슬 이동:
        • move() 함수를 사용해 구슬이 #(벽)을 만나거나 O(구멍)에 빠질 때까지 이동한다.
        • 빨간 구슬과 파란 구슬을 각각 이동시킨다.
      2. 파란 구슬이 구멍에 빠졌는지 확인
        • 파란 구슬이 구멍에 빠지면 해당 방향 탐색을 건너뛴다.
      3. 빨간 구슬이 구멍에 빠졌는지 확인
        • 빨간 구슬이 구멍에 빠지면 depth를 출력하고 프로그램을 종료한다.
      4. 구슬이 겹쳤는지 확인
        • 빨간 구슬과 파란 구슬이 같은 위치에 있다면, 더 많이 이동한 구슬을 한 칸 뒤로 이동시킨다.
      5. 방문 여부 확인 및 큐에 추가
        • 새로운 상태 (nrx, nry, nbx, nby)visited에 없다면 큐에 추가하고 방문 처리한다.
  5. 결과 처리
    • BFS 탐색을 모두 진행했음에도 목표 지점(빨간 구슬이 구멍에 빠지는 것)에 도달하지 못하면 1을 출력한다.

🚩제출한 코드

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

# 보드의 크기 입력
N, M = map(int, input().split())                                 # N: 세로, M: 가로
rect_board = [list(map(str, input().strip())) for _ in range(N)] # 직사각형 보드

# BFS 탐색 방향 (상, 우, 하, 좌)
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]

# 빨간 구슬(R)과 파란 구슬(B)의 초기 위치 찾기
rx, ry, bx, by = 0, 0, 0, 0
for i in range(N):
    for j in range(M):
        if rect_board[i][j] == 'R':
            rx, ry = i, j
        if rect_board[i][j] == 'B':
            bx, by = i, j

# BFS 탐색 준비
queue = deque([(rx, ry, bx, by, 1)])  # (rx, ry, bx, by, depth)
visited = set()
visited.add((rx, ry, bx, by)) # 방문 체크

# 구슬 이동 함수
def move(x, y, dx, dy):
    # 빨간 구슬과 파란 구슬을 보드에서 #이나 O를 만날 때까지 이동
    count = 0
    while rect_board[x + dx][y + dy] != '#' and rect_board[x][y] != 'O':
        x += dx
        y += dy
        count += 1 # 이동횟수
    return x, y, count


# BFS 탐색
while queue:
    rx, ry, bx, by, depth = queue.popleft()

    # 10번을 초과했다면 실패
    if depth > 10:
        print(-1)
        exit()

    # BFS 4방향 탐색
    for dx, dy in directions:
        # 빨간 구슬과 파란 구슬 이동
        nrx, nry, r_count = move(rx, ry, dx, dy)
        nbx, nby, b_count = move(bx, by, dx, dy)

        # 파란 구슬이 구멍에 빠지면 실패 -> 다음 방향 탐색
        if rect_board[nbx][nby] == 'O':
            continue

        # 빨간 구슬이 구멍에 빠지면 성공
        if rect_board[nrx][nry] == 'O':
            print(depth)
            exit()

        # 두 구슬이 겹칠 경우, 더 많이 이동한 구슬을 한 칸 뒤로
        if nrx == nbx and nry == nby:
            if r_count > b_count:
                nrx -= dx
                nry -= dy
            else:
                nbx -= dx
                nby -= dy


        # 방문하지 않은 경우에만 큐에 추가
        if (nrx, nry, nbx, nby) not in visited:
            queue.append((nrx, nry, nbx, nby, depth + 1))
            visited.add((nrx, nry, nbx, nby))
# 10번 이내에 성공하지 못했다면 -1 출력

print(-1)

💡TIL

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

  • 문제를 풀 때 빨간 구슬과 파란 구슬이 동시에 움직이는 점과 겹칠 때의 처리 방법이 가장 어려웠다. 이 점이 가장 막막하지 않았나 싶다.
  • 문제가 너무 안 풀려서 gpt한테 뭐가 문제냐고 물어봤는데 내가 O0(숫자 0)으로 착각하고 풀었던 게 문제였다… O나 0이나 그냥 문자로 보면 비슷해보여서 헷갈렸던 것 같다. 0인지 O인지 주의해서 풀어야겠다. 비슷한 문자에 주의하자!!
  • 방문여부 처리를 리스트로 미리 정의해놓는게 아니라 방문할 때마다 추가하는 방식의 문제라 신선했다.
    • 상태 방문 여부를 set튜플로 관리하면서 BFS를 효율적으로 구현할 수 있었다.
  • BFS는 구현이 다소 복잡했지만, 큐와 방문 처리 로직의 중요성을 다시 한번 깨달았다. 좋은 문제였다!

@github-actions
Copy link

github-actions bot commented Apr 8, 2025

2025-04 챌린지 진행 상황

사용자 챌린지 유형 문제 수 달성 여부
Mingguriguri 그래프 2
Mingguriguri DP 6

@Mingguriguri Mingguriguri reopened this Apr 8, 2025
@github-actions
Copy link

github-actions bot commented Apr 8, 2025

2025-04 챌린지 진행 상황

사용자 챌린지 유형 문제 수 달성 여부
Mingguriguri 그래프 2
Mingguriguri DP 6
zaqquum 그래프 0
zaqquum DP 2

@Mingguriguri Mingguriguri reopened this Apr 8, 2025
@Mingguriguri Mingguriguri merged commit 2b1cdad into main Apr 8, 2025
1 check failed
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.

2 participants