그리디(탐욕) 알고리즘
최적의 해를 구하는 데에 사용되는 근시안적인 방법이다.
각 단계에서 최적이라고 판단하는 것을 선택해 나가는 방식으로 최종적인 해답에 도달하는 알고리즘이다.
문제에 따라 최적해를 보장하기도 하지만, 일반적으로 근사 해를 구하는데 사용된다.
따라서 그리디 알고리즘을 사용하기 위해서는 부분 문제의 최적해가 전체 문제의 최적해를 보장하는 경우에 사용해야한다.
실생활에서는 이러한 문제가 상당히 많이 있기 때문에 많은 문제에 적용될 수 있다.
그리디 알고리즘의 조건
- 탐욕 선택 속성
- 매 순간 가장 좋아보이는 선택을 해도 전체 최적해를 구할 수 있는 성질
- 최적 부분 구조
- 작은 부분 문제의 해로 전체 문제의 최적해를 구할 수 있는 성질
그리디 알고리즘은 위의 두 성질을 만족해야한다.
dp의 경우에도 최적 부분 구조를 갖추고 있는데 dp와 다른 특징은 탐욕 선택 속성이다.
매순간 최적해가 곧 전체의 최적 해에 포함됨으로 매 순간의 부분해를 저장할 필요 없이 다음 문제의 해를 구해나간다.
다익스트라
각 간선의 가중치 합이 최소가 되는 두 정점 사이의 경로를 구하는 알고리즘이다.
즉, 두 정점간의 최단 거리(최소비용거리)를 구할 때 사용되는 알고리즘이다.
다익스트라는 주로 BFS 알고리즘을 통해 구현된다.
종착지에 먼저 도착한 경로가 최단 거리라면 다른 탐색보다 항상 최단 거리이므로 탐색을 종료할 수 있다.
반대로, 먼저 도착하더라도 같은 레벨에 있는 다른 탐색이 경로가 더 짧다면 다음 레벨에서 최단 거리가 나올 수 있으므로 탐색을 더 수행해야한다.
이러한 가능성으로 인해 다익스트라는 추가적인 공간에 방문한 노드와 방문하지 않은 노드를 구분하고,
시작점에서 각 노드에 도달한 최단 거리를 기록해두고 매 탐색마다 최단 거리로 경신해줘야한다.
[알고리즘 과정]
1. 출발노드와 도착 노드 설정
2. 최단 거리 테이블에 각 정점에 방문하는 최단거리를 무한대로 설정
3. 현재 위치에 인접한 노드를 방문
4. 가중치 비교를 통해 최단 거리 테이블 업데이트
다익스트라 문제 풀이(백준 1916)
문제
N개의 도시가 있다.
그리고 한 도시에서 출발하여 다른 도시에 도착하는 M개의 버스가 있다.
우리는 A번째 도시에서 B번째 도시까지 가는데 드는 버스 비용을 최소화 시키려고 한다.
A번째 도시에서 B번째 도시까지 가는데 드는 최소비용을 출력하여라. 도시의 번호는 1부터 N까지이다.
해결
시작점에서 종착지까지 가는데 최소비용을 출력해야하므로
다익스트라를 통해 풀어낼 수 있다.
가중치가 비용이 된다.
도시 방문 비용을 담는 별도의 배열을 할당하여 정점들을 탐색하며 최소 비용을 업데이트 해준다.
import java.io.*;
import java.util.*;
class Main {
// 간선 정보를 저장할 Node 클래스 (우선순위 큐에서 비용 기준 정렬 위해 Comparable 구현)
static class Node implements Comparable<Node> {
int to, cost;
Node(int to, int cost) {
this.to = to;
this.cost = cost;
}
// 비용이 낮은 순으로 정렬
public int compareTo(Node o) {
return this.cost - o.cost;
}
}
static int n, m; // 도시 수, 버스 수
static List<List<Node>> graph = new ArrayList<>(); // 인접 리스트 형태로 그래프 저장
static int[] dist; // 시작점에서 각 도시까지의 최소 비용 저장
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine()); // 도시 개수 입력
m = Integer.parseInt(br.readLine()); // 버스 정보 개수 입력
// 인접 리스트 초기화
for (int i = 0; i <= n; i++) graph.add(new ArrayList<>());
// 거리 배열 초기화 (최댓값으로)
dist = new int[n + 1];
Arrays.fill(dist, Integer.MAX_VALUE);
// 간선 정보 입력
for (int i = 0; i < m; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
int from = Integer.parseInt(st.nextToken()); // 출발 도시
int to = Integer.parseInt(st.nextToken()); // 도착 도시
int cost = Integer.parseInt(st.nextToken()); // 비용
graph.get(from).add(new Node(to, cost)); // from → to 간선 추가
}
// 시작 도시와 도착 도시 입력
StringTokenizer st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken());
int end = Integer.parseInt(st.nextToken());
// 다익스트라 알고리즘 실행
dijkstra(start);
// 최소 비용 출력
System.out.println(dist[end]);
}
// 다익스트라 알고리즘
static void dijkstra(int start) {
PriorityQueue<Node> pq = new PriorityQueue<>(); // 비용이 낮은 노드를 먼저 처리
pq.offer(new Node(start, 0)); // 시작 노드를 큐에 추가
dist[start] = 0; // 시작 노드까지 비용은 0
while (!pq.isEmpty()) {
Node now = pq.poll(); // 현재 비용이 가장 낮은 노드 꺼냄
// 이미 더 짧은 경로로 방문한 적 있다면 스킵
if (dist[now.to] < now.cost) continue;
// 현재 노드와 연결된 인접 노드들 탐색
for (Node next : graph.get(now.to)) {
// 더 짧은 경로가 있다면 갱신
if (dist[next.to] > dist[now.to] + next.cost) {
dist[next.to] = dist[now.to] + next.cost;
pq.offer(new Node(next.to, dist[next.to])); // 갱신된 거리로 큐에 추가
}
}
}
}
}
'자료구조 & 알고리즘 > 알고리즘' 카테고리의 다른 글
DP(동적계획법) (1) | 2025.06.15 |
---|---|
크루스칼과 프림 (최소신장 트리) (0) | 2025.06.15 |
[그래프 탐색] BFS와 DFS, 위상정렬 (0) | 2025.06.15 |
슬라이딩 윈도우 알고리즘 (0) | 2025.06.14 |
팰린드롬 (0) | 2025.06.14 |