Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

59-xxubin04 #220

Merged
merged 2 commits into from
Sep 25, 2024
Merged

59-xxubin04 #220

merged 2 commits into from
Sep 25, 2024

Conversation

xxubin04
Copy link
Member

@xxubin04 xxubin04 commented Sep 8, 2024

🔗 문제 링크

백준 21736번: 헌내기는 친구가 필요해


✔️ 소요된 시간

40분


✨ 수도 코드

1. 문제 이해

I: 도연이의 현재 위치
P: 사람의 위치
O: 빈 공간 (도연이가 움직일 수 있는 위치)
X: 벽 (도연이가 갈 수 없는 위치)


전형적인 BFS 문제이다. 도연이가 만날 수 있는 사람의 수를 구하면 된다.

2. 코드 분석

N, M = map(int, input().split())
campus = []
people = 0
visited = [[0 for _ in range(M)] for _ in range(N)]

캠퍼스의 크기인 N과 M을 입력받고, 캠퍼스의 정보들을 campus에 저장한다.



def bfs(graph, node_x, node_y,  visited):
    global people
    dx = [-1, 1, 0, 0]
    dy = [0, 0, -1, 1]

    if not visited[node_x][node_y]:
        visited[node_x][node_y] = 1

        for d in range(4):
            nx = node_x + dx[d]
            ny = node_y + dy[d]
            if 0 <= nx < N and 0 <= ny < M and not visited[nx][ny]:
                if campus[nx][ny] == "P":
                    people += 1
                    bfs(campus, nx, ny, visited)
                elif campus[nx][ny] == "O":
                    bfs(campus, nx, ny, visited)

캠퍼스 내부에서 방문한 적이 없는 좌표라면 방문처리를 해준다.

if 0 <= nx < N and 0 <= ny < M and not visited[nx][ny]:

그리고 해당 좌표에서 동서남북 좌표의 존재성과 방문여부를 확인해준다.

  • 해당 좌표가 P라면, people에 1을 더하고,(만날 수 있는 사람 수 1 추가) bfs 함수를 호출해준다.
  • 해당 좌표가 O라면, 그저 이동할 수 있는 좌표인 것이므로 bfs 함수를 호출해준다.


for i in range(N):
    campus.append(row := list(input().rstrip()))
    if "I" in row:
        row_I = i

bfs(campus, row_I, campus[row_I].index("I"), visited)
print(people) if people else print("TT")

N만큼 캠퍼스의 정보를 입력받고, 입력받은 정보에서 I가 등장하면 그 위치를 row_I로 저장해둔다.
I는 초기의 도연이의 위치이므로 bfs(campus, row_I, campus[row_I].index("I"), visited)으로 시작해준다.

people이 0이라면 "TT"를 출력하고, 아니라면 people을 출력한다.



3. 전체 코드

import sys
sys.setrecursionlimit(10**7)
input = open(0).readline

N, M = map(int, input().split())
campus = []
people = 0
visited = [[0 for _ in range(M)] for _ in range(N)]

def bfs(graph, node_x, node_y,  visited):
    global people
    dx = [-1, 1, 0, 0]
    dy = [0, 0, -1, 1]

    if not visited[node_x][node_y]:
        visited[node_x][node_y] = 1

        for d in range(4):
            nx = node_x + dx[d]
            ny = node_y + dy[d]
            if 0 <= nx < N and 0 <= ny < M and not visited[nx][ny]:
                if campus[nx][ny] == "P":
                    people += 1
                    bfs(campus, nx, ny, visited)
                elif campus[nx][ny] == "O":
                    bfs(campus, nx, ny, visited)

for i in range(N):
    campus.append(row := list(input().rstrip()))
    if "I" in row:
        row_I = i

bfs(campus, row_I, campus[row_I].index("I"), visited)
print(people) if people else print("TT")

📚 새롭게 알게된 내용

문제 제목이 딱 제 얘기같아서 풀게 되었습니다😁

@9kyo-hwang
Copy link
Collaborator

23학번이... 헌내기?

@xxubin04
Copy link
Member Author

xxubin04 commented Sep 8, 2024

23학번이... 헌내기?

23학번도 새내기로 쳐주나요? 아싸~~

Copy link
Collaborator

@9kyo-hwang 9kyo-hwang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음엔 코드 간편하게 짤려고 재귀 기반 dfs 코드로 구현했는데, 속도가 엄청나게 느리네요...

recursion dfs

import sys

def main(input):
    N, M = map(int, input().split())
    campus = [[None] * M for _ in range(N)]
    converter = {'O': 0, 'X': -1, 'I': 0, 'P': 1}
    
    start_x, start_y = 0, 0
    for i in range(N):
        for j, info in enumerate(input().rstrip()):
            if info == 'I':
                start_x, start_y = i, j
                
            campus[i][j] = converter[info]
    
    
    def out_of_bound(x: int, y: int) -> bool:
        return not (0 <= x < N and 0 <= y < M)
    
    
    def dfs(x: int, y: int) -> int:
        if out_of_bound(x, y) or campus[x][y] == -1:
            return 0
            
        is_human = (campus[x][y] == 1)
        campus[x][y] = -1
        
        return is_human + dfs(x - 1, y) + dfs(x, y + 1) + dfs(x + 1, y) + dfs(x, y - 1)
        
    num = dfs(start_x, start_y)
    print('TT' if num == 0 else num)


if __name__ == "__main__":
    sys.setrecursionlimit(10**6)
    main(sys.stdin.readline)

image

해서 queue 쓰는 bfs로 바꾸니까 속도가 확 빨라지네요...

queue bfs

from collections import deque

def bfs(campus: list, q: deque) -> int:
    def out_of_bound(x: int, y: int) -> bool:
        return not (0 <= x < N and 0 <= y < M)
    
    num = 0
    while q:
        x, y = q.popleft()
        for dx, dy in offset:
            nx, ny = x + dx, y + dy
            if out_of_bound(nx, ny) or campus[nx][ny] == -1:
                continue
            
            num += 1 if campus[nx][ny] == 1 else 0
            campus[nx][ny] = -1
            q.append((nx, ny))
            
    return num


if __name__ == "__main__":
    input = open(0).readline
    
    N, M = map(int, input().split())
    campus = [[None] * M for _ in range(N)]
    converter = {'O': 0, 'X': -1, 'I': -1, 'P': 1}
    offset = ((-1, 0), (0, 1), (1, 0), (0, -1))
    
    q = deque()
    for i in range(N):
        for j, info in enumerate(input().rstrip()):
            if info == 'I':
                q.append((i, j))
                
            campus[i][j] = converter[info]
    
    
    ans = bfs(campus, q)
    print('TT' if ans == 0 else ans)

image

설마? 하고 stack 쓰는 dfs로 하니까 셋 중 제일 빠르네요 ㅋㅋ

stack dfs

def dfs(campus: list, s: list) -> int:
    def out_of_bound(x: int, y: int) -> bool:
        return not (0 <= x < N and 0 <= y < M)
    
    
    num = 0
    while s:
        x, y = s.pop()
        for dx, dy in offset:
            nx, ny = x + dx, y + dy
            if out_of_bound(nx, ny) or campus[nx][ny] == -1:
                continue
            
            num += 1 if campus[nx][ny] == 1 else 0
            campus[nx][ny] = -1
            s.append((nx, ny))
            
    return num


if __name__ == "__main__":
    input = open(0).readline
    
    N, M = map(int, input().split())
    campus = [[None] * M for _ in range(N)]
    converter = {'O': 0, 'X': -1, 'I': -1, 'P': 1}
    offset = ((-1, 0), (0, 1), (1, 0), (0, -1))
    
    s = []
    for i in range(N):
        for j, info in enumerate(input().rstrip()):
            if info == 'I':
                s.append((i, j))
                
            campus[i][j] = converter[info]
    
    
    ans = dfs(campus, s)
    print('TT' if ans == 0 else ans)

image

재귀 코드가 가장 깔끔하긴 한데... 재귀 호출 횟수가 너무 많아서 확 느려진 게 아닐까 추측 중입니다...

people = 0
visited = [[0 for _ in range(M)] for _ in range(N)]

def bfs(graph, node_x, node_y, visited):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bfs가 아니라 dfs!

if "I" in row:
row_I = i

bfs(campus, row_I, campus[row_I].index("I"), visited)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개인적인 생각으론 row_I를 찾을 때 도연의 y 좌표도 구해놓는 게 좀 더 괜찮지 않을까 싶네요 :)

Comment on lines +12 to +13
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dx, dy 선언은 함수 밖으로 빼도 괜찮을 것 같아요. 지금 방식은 재귀 호출을 할 때마다 새로 정의되네요.

Copy link
Collaborator

@mjj111 mjj111 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

흡;;; 문제 제목이 마음에 안듭니다... 개인적으로 찔리거나 그렇지는 않구요..크흡...

파이썬은 재귀사용할 때, 런탐에러 주의해야해서 sys 설정을 넣어줘야하는걸 깜빡했습니다, ㅎ 5분 어버버 댔네요

import sys
sys.setrecursionlimit(10**6)
input = sys.stdin.readline

def can_go(nx, ny):
    return 0<=nx<n and 0<=ny<m and not visited[nx][ny]

def dfs(x,y):
    global cnt
    visited[x][y] = True
    if graph[x][y] == 'P':
        cnt+=1
    for i in range(4):
        nx = x + dx[i]
        ny = y + dy[i]
        
        if can_go(nx, ny):
            if graph[nx][ny] !="X":
                dfs(nx,ny)


dx = [-1,1,0,0]
dy = [0,0,-1,1]
cnt = 0
n,m = map(int,input().split())
graph = list(input() for _ in range(n))
visited = [[False]*m for _ in range(n)]

for i in range(n):
    for j in range(m):
        if graph[i][j]=="I":
            dfs(i,j)
if cnt == 0:
    print("TT") # 친구 없다고 코드로 놀리네요 ㅠ
else:
    print(cnt)

Copy link
Member

@gjsk132 gjsk132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

항상 bfs 쓰려다가 dfs써서... 이번에는 bfs로 풀어봤습니다!

visited로 따로 체크하지 않고, 문자열을 숫자로 바꾸어 배열에 넣었는데...
괜히 전체를 한 번 지나야하는게 주어진 값이 커지면 안 좋을 것 같다는 생각이 드네요...

다음에는 visited로 해보겠습니다!

전체 코드

input = open("input.txt").readline

from collections import deque

h, w = map(int,input().split())

campus = []
offset = [(-1, 0), (0, 1), (1, 0), (0, -1)]

dq = deque([])

answer = 0

for x in range(h):
    row = []
    for y, i in enumerate(input().rstrip()):
        if i == "O":
            row.append(0)
        elif i =="P":
            row.append(1)
        elif i =="X":
            row.append(2)
        else:
            row.append(3)
            dq.append((x, y))

    campus.append(row)

while dq:
    nx, ny = dq.popleft()
    for dx, dy in offset:
        px = nx + dx
        py = ny + dy

        if px < 0 or px >= h or py < 0 or py >= w:
            continue

        if campus[px][py] > 1:
            continue

        elif campus[px][py] == 1:
            answer += 1
        
        campus[px][py] = 2

        dq.append((px, py))

print(answer if not answer==0 else "TT")

@9kyo-hwang 9kyo-hwang merged commit 7cddadb into AlgoLeadMe:main Sep 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants