일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- react
- 리액트
- OS
- 자바스크립트
- Level2
- VUE
- CS
- 카카오
- C++
- sql
- 고득점Kit
- LeetCode
- python
- web
- javascript
- Level3
- 프로그래머스
- typescript
- Level1
- 파이썬
- 백준
- 코테연습
- Medium
- 웹프로그래밍
- 리트코드
- Doitvue.js입문
- 배열
- 프로그래밍
- 동적계획법
- dp
- Today
- Total
[프로그래머스] 전력망을 둘로 나누기 - python 본문
전력망을 둘로 나누기
문제
n개의 송전탑이 전선을 통해 하나의 트리 형태로 연결되어 있습니다. 당신은 이 전선들 중 하나를 끊어서 현재의 전력망 네트워크를 2개로 분할하려고 합니다. 이때, 두 전력망이 갖게 되는 송전탑의 개수를 최대한 비슷하게 맞추고자 합니다.
송전탑의 개수 n, 그리고 전선 정보 wires가 매개변수로 주어집니다. 전선들 중 하나를 끊어서 송전탑 개수가 가능한 비슷하도록 두 전력망으로 나누었을 때, 두 전력망이 가지고 있는 송전탑 개수의 차이(절대값)를 return 하도록 solution 함수를 완성해주세요.
제한사항
- n은 2 이상 100 이하인 자연수입니다.
- wires는 길이가 n-1인 정수형 2차원 배열입니다.
- wires의 각 원소는 [v1, v2] 2개의 자연수로 이루어져 있으며, 이는 전력망의 v1번 송전탑과 v2번 송전탑이 전선으로 연결되어 있다는 것을 의미합니다.
- 1 ≤ v1 < v2 ≤ n 입니다.
- 전력망 네트워크가 하나의 트리 형태가 아닌 경우는 입력으로 주어지지 않습니다.
풀이
처음에는 1개씩 끊어보면서 union-find해서 각각 집합의 개수를 구한 후 개수 차가 작은 것을 찾으면 어떨까 했다.
그런데 집합이 2개보다 더 크게 나눠지는 경우 처리를 할 수가 없어서 포기했다. Union-find으로도 풀 수 있는 방법이 있을 것 같은데.. 애초에 왜 2개로 안 나눠졌던 건지 모르겠다.
다음으로는 간선을 하나씩 제거해서 그래프를 만들어 보면서 dfs를 돌아서 각 개수 차의 최소를 구했다. dfs를 돌면서 노드의 수를 어떻게 세야할지 모르겠어서 좀 헤맸는데 전역 변수를 써서 해결했다.
cnt = 1
def dfs(graph, visited, start):
global cnt
for next_node in graph[start]:
if not visited[next_node]:
visited[next_node] = True
cnt += 1
dfs(graph, visited, next_node)
def solution(n, wires):
global cnt
answer = float('inf')
for w in wires:
tmp = wires[:]
tmp.remove(w)
graph = [[] for _ in range(n + 1)]
for a, b in tmp:
graph[a].append(b)
graph[b].append(a)
visited = [False] * (n + 1)
networks = []
for node in range(1, n + 1):
if not visited[node]:
visited[node] = True
cnt = 1
dfs(graph, visited, node)
networks.append(cnt)
answer = min(answer, abs(networks[0] - networks[1]))
return answer
다른 사람 풀이
다른 사람 풀이 중에 union-find로 푼 풀이
uf에 root 노드에는 자식 노드의 개수가 저장되고 자식 노드는 루트 노드의 번호를 가지게 된다.
uf = []
def find(a):
global uf
if uf[a] < 0: return a
uf[a] = find(uf[a])
return uf[a]
def merge(a, b):
global uf
pa = find(a)
pb = find(b)
if pa == pb: return
uf[pa] += uf[pb]
uf[pb] = pa
def solution(n, wires):
global uf
answer = int(1e9)
k = len(wires)
for i in range(k):
uf = [-1 for _ in range(n+1)]
tmp = [wires[x] for x in range(k) if x != i]
for a, b in tmp: merge(a, b)
v = [x for x in uf[1:] if x < 0]
answer = min(answer, abs(v[0]-v[1]))
return answer
+) 문제를 찾았다 !!!!!
이전 코드의 경우 find를 할 때 경로 압축을 하지 않았다. 그리고 부모를 찾을 때 경로 압축을 했더라도 간선이 순서대로 주어지지 않으면 부모 배열을 참조했을 때 바로 이전 부모로 나오기 때문에 부모 배열을 봤을 때는 최소 신장 트리가 2개로 나눠지지 않은 것처럼 보였던 것이다.
예) [[1, 4], [2, 5], [5, 1], [6, 3]]
parnets) [0, 1, 1, 3, 1, 2, 3]
따라서 union 연산을 한 후 각 노드에 대하여 find 연산을 하면서 루트 노드를 확인해서 카운트 해준다.
from collections import Counter
def find(x, p):
if p[x] != x:
p[x] = find(p[x], p)
return p[x]
def union(a, b, p):
a = find(a, p)
b = find(b, p)
if a < b:
p[b] = a
else:
p[a] = b
def solution(n, wires):
answer = float('inf')
for w in wires:
tmp = wires[:]
tmp.remove(w)
p = [i for i in range(n + 1)]
for a, b in tmp:
if find(a, p) == find(b, p):
continue
union(a, b, p)
uf = []
for i in range(1, n + 1):
uf.append(find(i, p))
uf = Counter(uf)
v = list(uf.values())
answer = min(answer, abs( v[0]- v[1]))
return answer
'코테 문제 풀이' 카테고리의 다른 글
[프로그래머스] 피보나치 수 - python (0) | 2022.04.24 |
---|---|
[프로그래머스] 땅따먹기 - python (0) | 2022.04.24 |
[프로그래머스] 올바른 괄호 - python (0) | 2022.04.23 |
[프로그래머스] 양궁대회 - python (0) | 2022.04.21 |
[프로그래머스] 주차 요금 계산 - python (0) | 2022.04.21 |