View
📚 문제
한 마을은 N개의 집과 M개의 도로로 구성되어 있습니다. 각 집은 0번부터 N-1번까지의 번호로 구분됩니다. 모든 도로에는 가로등이 구비되어 있는데, 특정한 도로의 가로등을 하루 동안 켜기 위한 비용은 해당 도로의 길이와 동일합니다. 예를 들어 2번 집과 3번 집 사이를 연결하는 길이가 7인 도로가 있다고 해봅시다. 하루 동안 이 가로등을 켜기 위한 비용은 7이 됩니다.
정부에서는 일부 가로등을 비활성화하되, 마을에 있는 임의의 두 집에 대하여 가로등이 켜진 도로만으로도 오갈 수 있도록 만들고자 합니다. 결과적으로 일부 가로등을 비활성화하여 최대한 많은 금액을 절약하고자 합니다. 마을의 집과 도로 정보가 주어졌을 때, 일부 가로등을 비활성화하여 절약할 수 있는 최대 금액을 출력하는 프로그램을 작성하세요.
- 첫째 줄에 집의 수 N(1<=N<=200,000)과 도로의 수 M(N-1<=M<=200,000)이 주어집니다.
- 다음 M개의 줄에 걸쳐서 각 도로에 대한 정보 X, Y, Z가 주어지며 공백으로 구분합니다. ( X번 집과 Y번 집 사이에 양방향 도로의 길이가 Z라는 의미)
예제 입력 | 예제 출력 |
7 11 0 1 7 0 3 5 1 2 8 1 3 9 1 4 7 2 4 5 3 4 15 3 5 6 4 5 8 4 6 9 5 6 11 |
51 |
📝 문제 해결
이 문제에서는 가로등이 켜진 도로만을 이용해서 모든 두 집이 서로 도달이 가능해야 한다는 조건을 제시하고 있음.
이때 최소한의 비용으로 모든 집을 연결해야 하기 때문에 최소 신장 트리로 해결할 수 있음. 따라서 주어진 입력을 통해서 그래프를 구성한 뒤에 크루스칼 알고리즘을 수행하면 된다.
이 문제처럼 '왕래'할 수 있다는 것은 그래프에서 각 노드가 서로 연결되어 있다는 의미로 최소 신장 트리를 의심해봐야 함.
💻 코드
import java.util.*;
class Edge implements Comparable<Edge> {
private int distance;
private int nodeA;
private int nodeB;
public Edge(int distance, int nodeA, int nodeB) {
this.distance = distance;
this.nodeA = nodeA;
this.nodeB = nodeB;
}
public int getDistance() {
return this.distance;
}
public int getNodeA() {
return this.nodeA;
}
public int getNodeB() {
return this.nodeB;
}
// 거리(비용)가 짧은 것이 높은 우선순위를 가지도록 설정
@Override
public int compareTo(Edge other) {
if (this.distance < other.distance) {
return -1;
}
return 1;
}
}
public class Main {
// 노드의 개수와 간선의 개수
public static int n, m;
public static int[] parent = new int[200001]; // 부모 테이블 초기화하기
// 모든 간선을 담을 리스트와, 최종 비용을 담을 변수
public static ArrayList<Edge> edges = new ArrayList<>();
public static int result = 0;
// 특정 원소가 속한 집합을 찾기
public static int findParent(int x) {
// 루트 노드가 아니라면, 루트 노드를 찾을 때까지 재귀적으로 호출
if (x == parent[x]) return x;
return parent[x] = findParent(parent[x]);
}
// 두 원소가 속한 집합을 합치기
public static void unionParent(int a, int b) {
a = findParent(a);
b = findParent(b);
if (a < b) parent[b] = a;
else parent[a] = b;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
// 부모 테이블상에서, 부모를 자기 자신으로 초기화
for (int i = 1; i <= n; i++) {
parent[i] = i;
}
// 모든 간선에 대한 정보를 입력 받기
for (int i = 0; i < m; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
int z = sc.nextInt();
edges.add(new Edge(z, x, y));
}
// 간선을 비용순으로 정렬
Collections.sort(edges);
int total = 0; // 전체 가로등 비용
// 간선을 하나씩 확인하며
for (int i = 0; i < edges.size(); i++) {
int cost = edges.get(i).getDistance();
int a = edges.get(i).getNodeA();
int b = edges.get(i).getNodeB();
total += cost;
// 사이클이 발생하지 않는 경우에만 집합에 포함
if (findParent(a) != findParent(b)) {
unionParent(a, b);
result += cost;
}
}
// 절약할 수 있는 최대 금액 = 전체 가로등을 켜는 비용 - 최소 신장 트리를 구성하는 비용
System.out.println(total - result);
}
}
728x90
'알고리즘 > 이코테' 카테고리의 다른 글
09. 최단 경로 - 플로이드 위셜 알고리즘 (0) | 2023.01.05 |
---|---|
10. 그래프 이론 - 최소 신장 트리 알고리즘 (0) | 2022.12.31 |
[동적 계획법] 이코테 개미 전사(Java) (0) | 2022.12.21 |
reply