알고리즘/백준

[백준] 17472번 : 다리 만들기 2

bigkwangs 2023. 7. 31. 12:44
728x90

https://www.acmicpc.net/problem/17472

 

17472번: 다리 만들기 2

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.

www.acmicpc.net

해당 문제는 각각의 그룹을 나누는 과정을 너비우선탐색으로 구하였고, 그룹 A에서 그룹 B까지 갈 수 있는 다리 경로를 모두 구한다. 그러니까 특정 그룹에서 다른그룹까지 갈 수 있는 다리의 경로를 깊이우선탐색으로 구한다.

다리의 경로를 구했으면 해당 그룹으로부터 다른그룹까지 다 연결되어있으면서 최소거리를 구해야하니, 최소신장트리를 이용하면 된다.

코드로 구현하면 다음과 같다.

from collections import deque
move = [(1,0), (-1,0), (0,1), (0,-1)]
N, M = map(int, input().split()) # 세로, 가로
graph = [list(map(int, input().split())) for _ in range(N)]

def findGroup(i,j):
    for k in range(len(groupList)):
        for node in groupList[k]:
            if node[0] == i and node[1] == j:
                return k

def dfs(y,x, mode, cost): # 같은 그룹인지 체크하는 로직
    if graph[y][x] == 1: # 종료조건
        if cost == 1 or cost == 2:
            return None
        elif cost >= 3:
            return cost - 1, findGroup(y,x)
    
    dy, dx = move[mode]
    ny = dy + y
    nx = dx + x
    
    if N > ny >=0 and M > nx >=0:
        return dfs(ny,nx, mode, cost + 1)
    
    return None

def bfs(i,j):
    group = []
    queue = deque()
    if not visited[i][j] and graph[i][j] == 1:
        queue.append([i,j])
        group.append([i,j])
        visited[i][j] = True
        while queue:
            y , x = queue.popleft()
            for dy, dx in move:
                ny = dy + y
                nx = dx + x
                if N > ny >=0 and M > nx >=0 and not visited[ny][nx] and graph[ny][nx] == 1:
                    visited[ny][nx] = True
                    queue.append([ny,nx])
                    group.append([ny,nx])

        return group
    else:
        return None
    
def getParent(v1, parent):
    if v1 != parent[v1]:
        parent[v1] = getParent(parent[v1], parent)
    return parent[v1]
    
def union(v1,v2, parent):
    a = getParent(v1, parent)
    b = getParent(v2, parent)
    
    if a > b: # 더 작은 노드를 parent값을 선정한다.
        parent[a] = b
    else:
        parent[b] = a
        
groupList = []
visited = [[False for _ in range(M)] for _ in range(N)]
for i in range(N):
    for j in range(M):
        result = bfs(i,j)
        if result != None:
            groupList.append(result)
    
nodegraph = []
for k in range(len(groupList)):
    for node in groupList[k]:
        y, x = node
        for i in range(4): # 가로,세로를 돌려본다
            result = dfs(y,x,i, 0)
            if result != None:
                nodegraph.append((result[0],k,result[1])) # cost, 시작, 종료
                
nodegraph.sort()
parent = [v1 for v1 in range(len(groupList))] # 자기자신을 부모노드로 초기화
result = 0
counter = 0
for cost, start, end in nodegraph:
    if getParent(start, parent) != getParent(end, parent):
        union(start, end, parent)
        result += cost
        counter += 1
        
if counter == len(groupList)-1: # 노드-1 의 개수와 간선의 개수가 같으면 최소신장트리 조건
    print(result)
else:
    print(-1)

 

유니온을 하는과정에서 parent[v1] = b , parent[v2] = a로 초기화 하는 실수를 하고 말았다.

getParent로 parent가 바뀌게된다면 parent가 꼬이게 된다.

    if a > b: # 더 작은 노드를 parent값을 선정한다.
        parent[v1] = b
    else:
        parent[v2] = a

->


def union(v1,v2, parent):
    a = getParent(v1, parent)
    b = getParent(v2, parent)
    
    if a > b: # 더 작은 노드를 parent값을 선정한다.
        parent[a] = b
    else:
        parent[b] = a
728x90