본문 바로가기
문제 해결/BOJ

[백준] 물대기

by 자잘 2023. 2. 3.

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

 

1368번: 물대기

첫 줄에는 논의 수 N(1 ≤ N ≤ 300)이 주어진다. 다음 N개의 줄에는 i번째 논에 우물을 팔 때 드는 비용 Wi(1 ≤ Wi ≤ 100,000)가 순서대로 들어온다. 다음 N개의 줄에 대해서는 각 줄에 N개의 수가 들어

www.acmicpc.net

문제의 특징을 생각해봤을 때 논에 물을 끌어오려면 연결 그래프여야하고 각각의 연결 비용을 합쳤을 때 최소비용이 되어야하므로 자연스럽게 최소 신장 트리로 풀어야한다고 생각했습니다. 문제는 직접 논에 우물을 파는 것을 처리해야하는데 이 부분은 별도의 노드(코드에서는 other_node)를 만들어서 우물을 팔 때 발생하는 비용으로 각 노드와 연결시켰습니다. 이렇게 구축한 다음 프림 알고리즘을 돌리면 됩니다.

프림

import sys, heapq
from collections import defaultdict

input = sys.stdin.readline
INF = sys.maxsize
n = int(input())

wells = [-1] * (n + 1)


# 우물을 파는데 필요한 비용
for i in range(1, n + 1):
    wells[i] = int(input())

# 우물간의 인접 행렬
adj = [
    list(map(int, input().split()))
    for _ in range(n)
]

graph = defaultdict(list)


# 물을 길러올 별도의 노드
other_node = n + 1

def init():
    # 우물간의 경로를 설정
    for i in range(1, n + 1):
        for j in range(i + 1, n + 1):
            graph[i].append((j, adj[i - 1][j - 1]))
            graph[j].append((i, adj[i - 1][j - 1]))

    # 물을 길러오는 경우
    for i in range(1, n + 1):
        graph[other_node].append((i, wells[i]))
        graph[i].append((other_node, wells[i]))


def prim():
    # 1 ~ n + 1까지의 노드를 사용
    dist = [INF] * (n + 2)
    dist[other_node] = 0
    q = [(0, other_node)]
    visited = [False] * (n + 2)

    while q:
        d, v = heapq.heappop(q)

        if dist[v] < d:
            continue

        visited[v] = True

        for next_v, next_d in graph[v]:
            if not visited[next_v] and dist[next_v] > next_d:
                dist[next_v] = next_d
                heapq.heappush(q, (next_d, next_v))
    return sum(dist[1:])

init()
print(prim())

 

크루스칼

import sys, os

# sys.stdin = open(os.curdir + "\\input.txt", "r")

input = sys.stdin.readline

n = int(input())


# 우물을 파는데 드는 비용
well_price = [-1] * (n + 1)

edges = []

# 우물을 파야하는 노드
other_node = n + 1

# 우물에 해당하는 노드와 연결되어 있는 edge들을 넣음
for i in range(1, n + 1):
    price = int(input())
    edges.append((price, i, other_node))

# 인접 행렬 입력
adj = [[0] * (n + 1)] + [
    [0] + list(map(int, input().split()))
    for _ in range(n + 1)
]

# 인접 행렬의 edge들을 추가
for i in range(1, n + 1):
    for j in range(i + 1, n + 1):
        edges.append((adj[i][j], i, j))

edges.sort()
uf = [x for x in range(n + 2)]


def find(v):
    if uf[v] == v:
        return v

    uf[v] = find(uf[v])
    return uf[v]


answer = 0

for price, v, w in edges:
    # 부모 즉, 속해있는 집합을 찾는다.
    parent_w = find(w)
    parent_v = find(v)

    # 부모가 같은 경우는 이미 w, v가 MST에 속해있으므로 넘어간다.
    if parent_v == parent_w:
        continue

    answer += price

    if parent_v <= parent_w:
        uf[parent_w] = parent_v
    else:
        uf[parent_v] = parent_w

print(answer)

'문제 해결 > BOJ' 카테고리의 다른 글

[백준] 트리의 지름  (0) 2023.02.04
[백준] 낚시왕  (0) 2023.02.03
[BOJ] 일요일 아침의 데이트  (0) 2023.02.02
[백준] 10986번 나머지 합 - python  (1) 2022.09.29
[BOJ] 3980번 선발 명단  (0) 2019.08.20