Algorithm/BOJ

[BOJ/Python] 16562_친구비

O773H 2023. 2. 14. 23:26
728x90

문제 출처:https://www.acmicpc.net/problem/16562

 

16562번: 친구비

첫 줄에 학생 수 N (1 ≤ N ≤ 10,000)과 친구관계 수 M (0 ≤ M ≤ 10,000), 가지고 있는 돈 k (1 ≤ k ≤ 10,000,000)가 주어진다. 두번째 줄에 N개의 각각의 학생이 원하는 친구비 Ai가 주어진다. (1 ≤ Ai ≤ 10,

www.acmicpc.net

문제

19학번 이준석은 학생이 N명인 학교에 입학을 했다. 준석이는 입학을 맞아 모든 학생과 친구가 되고 싶어한다. 하지만 준석이는 평생 컴퓨터랑만 대화를 하며 살아왔기 때문에 사람과 말을 하는 법을 모른다. 그런 준석이에게도 희망이 있다. 바로 친구비다!

학생 i에게 Ai만큼의 돈을 주면 그 학생은 1달간 친구가 되어준다! 준석이에게는 총 k원의 돈이 있고 그 돈을 이용해서 친구를 사귀기로 했다. 막상 친구를 사귀다 보면 돈이 부족해질 것 같다는 생각을 하게 되었다. 그래서 준석이는 “친구의 친구는 친구다”를 이용하기로 했다.

준석이는 이제 모든 친구에게 돈을 주지 않아도 된다!

위와 같은 논리를 사용했을 때, 가장 적은 비용으로 모든 사람과 친구가 되는 방법을 구하라.

입력

첫 줄에 학생 수 N (1 ≤ N ≤ 10,000)과 친구관계 수 M (0 ≤ M ≤ 10,000), 가지고 있는 돈 k (1 ≤ k ≤ 10,000,000)가 주어진다.

두번째 줄에 N개의 각각의 학생이 원하는 친구비 Ai가 주어진다. (1 ≤ Ai ≤ 10,000, 1 ≤ i ≤ N)

다음 M개의 줄에는 숫자 v, w가 주어진다. 이것은 학생 v와 학생 w가 서로 친구라는 뜻이다. 자기 자신과 친구일 수도 있고, 같은 친구 관계가 여러 번 주어질 수도 있다.

출력

준석이가 모든 학생을 친구로 만들 수 있다면, 친구로 만드는데 드는 최소비용을 출력한다. 만약 친구를 다 사귈 수 없다면, “Oh no”(따옴표 제거)를 출력한다.

예제 입력 1

5 3 20
10 10 20 20 30
1 3
2 4
5 4

예제 출력 1

20

예제 입력 2

5 3 10
10 10 20 20 30
1 3
2 4
5 4

예제 출력 2

Oh no

풀이

 

친구들을 그룹별로 나누어, 각 그룹마다 친구비가 가장 저렴한 친구와 친구가 되면 된다.

예제에 대한 풀이는 다음과 같다.

우선 5명의 친구와 각 사람들마다 친구비가 존재한다.

1번과 3번은 친구이므로 연결되어 있다고 생각할 수 있다.

마찬가지로 2번과 4번 또한 친구이므로 연결되어있다.

2번과 5번 또한 친구이므로 연결되어 있다.

  • 따라서 현재 친구들의 연결상태를 통해 친구들의 그룹을 나누게 되면 두 그룹으로 나눌 수 있게 된다.
  • 그렇다면 여기서 각 그룹마다 한 명만 친구가 된다면 그 그룹에 있는 다른 사람들과는 친구비 없이 친구가 될 수 있다.
  • 그러면 간단하게 친구비가 가장 저렴한 사람과 친구가 되면 된다.

 

  • 각 그룹별로 이렇게 두 사람이 친구비가 가장 저렴하므로, 모든 사람들과 친구가 되기 위해서 총 20의 비용이 필요하다.

코드는 다음과 같다.

 

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
32
33
34
35
36
37
38
39
import sys
 
def find_parent(parent,x):
    if parent[x]!=x:
        parent[x] = find_parent(parent,parent[x])
    return parent[x]
 
def union(parent,cost,a,b):
    a = find_parent(parent,a)
    b = find_parent(parent,b)
 
    if a<b:
        parent[b] = a
        cost[a] = min(cost[a],cost[b])
    else:
        parent[a] = b
        cost[b] = min(cost[a],cost[b])
 
n,m,k = map(int,sys.stdin.readline().split())
parent = [i for i in range(n+1)]
 
cost = [0]
cost.extend(list(map(int,sys.stdin.readline().split())))
 
for _ in range(m):
    a,b=map(int,sys.stdin.readline().split())
    union(parent,cost,a,b)
 
result = 0
 
for i in range(1,n+1):
    if parent[i]==i:
        result+=cost[i]
 
 
if result>k:
    print("Oh no")
else:
    print(result)
cs
  • union & find 를 이용하여 친구들을 그룹화 하였다.
  • 여기서 cost는 각 친구들의 친구비를 저장한 리스트이다.
  • 각 그룹마다 최소의 친구비를 구해야 하므로, union함수를 통해 그룹화를 할 때, 부모가 되는 노드에 친구비를 더 작은 값으로 변경해주었다.
  • 최종적으로 반복문을 이용하여, 만약 자신이 부모 노드라면 그 노드의 친구비가 그 그룹에서의 친구비 중 제일 작은 값이 저장되어 있으므로 result에 더해준다.
  • 최종적으로 result에는 모든 그룹에서 가장 저렴한 친구비의 합이 되고, 가지고 있는 돈 k와 비교한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90