λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

Algorithm

[λ°±μ€€ 1162] λ„λ‘œν¬μž₯ (Dijkstra, λ‹€μ΅μŠ€νŠΈλΌ)

λ°˜μ‘ν˜•
SMALL

🧐 λ°±μ€€ 1162.  λ„λ‘œν¬μž₯

문제

μ€€μ˜μ΄λŠ” 맀일 μ„œμšΈμ—μ„œ ν¬μ²œκΉŒμ§€ μΆœν‡΄κ·Όμ„ ν•œλ‹€. ν•˜μ§€λ§Œ 잠이 λ§Žμ€ μ€€μ˜μ΄λŠ” λŠ¦μž μ„ 자 ν¬μ²œμ— 늦게 λ„μ°©ν•˜κΈ° 일쑀닀. 돈이 λ§Žμ€ μ€€μ˜μ΄λŠ” κ³ λ―Ό 끝에 K개의 λ„λ‘œλ₯Ό 포μž₯ν•˜μ—¬ μ„œμšΈμ—μ„œ ν¬μ²œκΉŒμ§€ κ°€λŠ” μ‹œκ°„μ„ λ‹¨μΆ•ν•˜λ € ν•œλ‹€.
λ¬Έμ œλŠ” N개의 λ„μ‹œκ°€ 주어지고 κ·Έ 사이 λ„λ‘œμ™€ μ΄ λ„λ‘œλ₯Ό 톡과할 λ•Œ κ±Έλ¦¬λŠ” μ‹œκ°„μ΄ μ£Όμ–΄μ‘Œμ„ λ•Œ μ΅œμ†Œ μ‹œκ°„μ΄ 걸리도둝 ν•˜λŠ” K개의 μ΄ν•˜μ˜ λ„λ‘œλ₯Ό 포μž₯ν•˜λŠ” 것이닀. λ„λ‘œλŠ” 이미 μžˆλŠ” λ„λ‘œλ§Œ 포μž₯ν•  수 있고, ν¬μž₯ν•˜κ²Œ 되면 λ„λ‘œλ₯Ό μ§€λ‚˜λŠ”λ° κ±Έλ¦¬λŠ” μ‹œκ°„μ΄ 0이 λœλ‹€. λ˜ν•œ νŽΈμ˜μƒ μ„œμšΈμ€ 1번 λ„μ‹œ, ν¬μ²œμ€ N번 λ„μ‹œλΌ ν•˜κ³  1λ²ˆμ—μ„œ Nλ²ˆκΉŒμ§€ 항상 갈 수 μžˆλŠ” λ°μ΄ν„°λ§Œ 주어진닀.

μž…λ ₯

첫 μ€„μ—λŠ” λ„μ‹œμ˜ 수 N(1 ≤ N ≤ 10,000)κ³Ό λ„λ‘œμ˜ 수 M(1 ≤ M ≤ 50,000)κ³Ό 포μž₯ν•  λ„λ‘œμ˜ 수 K(1 ≤ K ≤ 20)κ°€ 곡백으둜 κ΅¬λΆ„λ˜μ–΄ 주어진닀. M개의 쀄에 λŒ€ν•΄ λ„λ‘œκ°€ μ—°κ²°ν•˜λŠ” λ‘ λ„μ‹œμ™€ λ„λ‘œλ₯Ό ν†΅κ³Όν•˜λŠ”λ° κ±Έλ¦¬λŠ” μ‹œκ°„μ΄ 주어진닀. λ„λ‘œλ“€μ€ μ–‘λ°©ν–₯ λ„λ‘œμ΄λ©°, κ±Έλ¦¬λŠ” μ‹œκ°„μ€ 1,000,000보닀 μž‘κ±°λ‚˜ 같은 μžμ—°μˆ˜μ΄λ‹€.

예제

예제 μž…λ ₯ 예제 좜λ ₯
4 4 1
1 2 10
2 4 10
1 3 1
3 4 100
1

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

 

1162번: λ„λ‘œν¬μž₯

첫 μ€„μ—λŠ” λ„μ‹œμ˜ 수 N(1 ≤ N ≤ 10,000)κ³Ό λ„λ‘œμ˜ 수 M(1 ≤ M ≤ 50,000)κ³Ό 포μž₯ν•  λ„λ‘œμ˜ 수 K(1 ≤ K ≤ 20)κ°€ 곡백으둜 κ΅¬λΆ„λ˜μ–΄ 주어진닀. M개의 쀄에 λŒ€ν•΄ λ„λ‘œκ°€ μ—°κ²°ν•˜λŠ” λ‘ λ„μ‹œμ™€ λ„λ‘œλ₯Ό ν†΅κ³Όν•˜

www.acmicpc.net

 

πŸ’‘ 문제 풀이

λ‹¨μˆœν•œ λ‹€μ΅μŠ€νŠΈλΌ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œλŠ” 풀리지 μ•ŠλŠ” λ¬Έμ œμ˜€λ‹€. μ΅œλ‹¨ 거리 배열을 μ—…λ°μ΄νŠΈ ν•  λ•Œλ§ˆλ‹€ 경둜λ₯Ό 리슀트 ν˜•μ‹μœΌλ‘œ μ €μž₯ν•΄μ£ΌλŠ” λ“± μ—¬λŸ¬ 방법을 μƒκ°ν•΄λ΄€μ§€λ§Œ 풀이 쀑간에 ν•œ λ²ˆμ”©μ€ λ§‰νžˆλŠ” λ°©λ²•λ“€μ΄μ—ˆλ‹€. κ²°κ΅­ ꡬ글링을 톡해 μ°Ύμ•„λ³΄λ‹ˆ DPλ₯Ό ν•¨κ»˜ μ‚¬μš©ν•΄μ„œ ν’€ 수 μžˆλŠ” λ¬Έμ œμ˜€λ‹€.

 

λ‹¨μˆœ λ‹€μ΅μŠ€νŠΈλΌ μœ ν˜•κ³ΌλŠ” λ‹€λ₯΄κ²Œ Edge 클래슀λ₯Ό μ •μ˜ν•œλ‹€. ν•΄λ‹Ή ν΄λž˜μŠ€λŠ” ν˜„μž¬ 지점, λ„λ‘œ 포μž₯ 개수, 총 거리 λΉ„μš© 정보λ₯Ό ν¬ν•¨ν•œλ‹€.

λ˜ν•œ, cost κΈ°μ€€μœΌλ‘œ μ˜€λ¦„μ°¨μˆœ 정렬될 수 μžˆλ„λ‘ compareTo λ©”μ†Œλ“œλ„ μ˜€λ²„λΌμ΄λ“œ ν•΄μ€€λ‹€.

static class Edge implements Comparable<Edge> {
	int end; // 도착(ν˜„μž¬) 지점
	int count; // λ„λ‘œ 포μž₯ 개수
	long cost; // 총 거리 λΉ„μš©

	Edge(int end, int count, long cost) {
		this.end = end;
		this.count = count;
		this.cost = cost;
	}

	@Override
	public int compareTo(Edge o) {
		return Long.compare(this.cost, o.cost);
	}
}

 

λ‹€μŒμ€ μ΄ˆκΈ°ν™” 단계이닀.

λ„λ‘œ 정보λ₯Ό μ €μž₯ν•˜λŠ” edges, k개 λ„λ‘œλ₯Ό 포μž₯ν•˜κ³  i번 μ§€μ κΉŒμ§€ κ°€λŠ” 데 κ±Έλ¦¬λŠ” μ΅œλ‹¨ 거리 λΉ„μš©μ„ μ €μž₯ν•˜λŠ” dpλ₯Ό μ΄ˆκΈ°ν™” ν•œλ‹€.

List<List<Edge>> edges = new ArrayList<>(); // λ„λ‘œ 정보
long[][] dp = new long[N + 1][K + 1]; // dp[i][k] : k개 λ„λ‘œλ₯Ό 포μž₯ν•΄μ„œ i번 μ§€μ κΉŒμ§€ κ°€λŠ” μ΅œλ‹¨ 거리 λΉ„μš©

// edges, dp μ΄ˆκΈ°ν™”
for (int i = 0; i <= N; i++) {
	edges.add(new ArrayList<>());
	Arrays.fill(dp[i], Long.MAX_VALUE);
}

for (int i = 0; i < M; i++) { // λ„λ‘œ 정보 μ €μž₯
	st = new StringTokenizer(br.readLine());
	int v1 = Integer.parseInt(st.nextToken());
	int v2 = Integer.parseInt(st.nextToken());
	int c = Integer.parseInt(st.nextToken());
	edges.get(v1).add(new Edge(v2, 0, c));
	edges.get(v2).add(new Edge(v1, 0, c));
}

 

μ΄ˆκΈ°ν™” ν›„ λ‹€μ΅μŠ€νŠΈλΌ μ•Œκ³ λ¦¬μ¦˜μ„ μ‹€ν–‰ν•˜λ©° dp 배열을 μ—…λ°μ΄νŠΈ μ‹œμΌœμ€€λ‹€.

Queue<Edge> pq = new PriorityQueue<>();
pq.add(new Edge(1, 0, 0)); // μ„œμšΈ 지점, 포μž₯ 개수 0, 거리 0
dp[1][0] = 0; // 0개 λ„λ‘œ 포μž₯ν•˜κ³  1번(μ„œμšΈ) μ§€μ κΉŒμ§€

while (!pq.isEmpty()) {
	Edge now = pq.poll();
	if (dp[now.end][now.count] < now.cost) continue;
	for (Edge next : edges.get(now.end)) {
		if (dp[next.end][now.count] > now.cost + next.cost) {
			dp[next.end][now.count] = now.cost + next.cost;
			pq.add(new Edge(next.end, now.count, dp[next.end][now.count]));
		}

		if (now.count < K && dp[next.end][now.count + 1] > now.cost) {
			dp[next.end][now.count + 1] = now.cost;
			pq.add(new Edge(next.end, now.count + 1, now.cost));
		}
	}
}

μš°μ„ μˆœμœ„ 큐λ₯Ό μ‚¬μš©ν•˜μ—¬ cost 값이 μž‘μ€ μˆœμ„œλŒ€λ‘œ μ²˜λ¦¬ν•œλ‹€. (μ—¬κΈ°μ„œ dp 값이 now.cost 보닀 이미 μž‘μœΌλ©΄ λ„˜μ–΄κ°„λ‹€.)

λ„λ‘œλ₯Ό μ‚¬μš©ν•˜μ—¬ λ„˜μ–΄κ°„ μ§€μ μ˜ dp 값을 2가지 경우둜 μ—…λ°μ΄νŠΈ μ‹œμΌœμ€€λ‹€.

μ²«λ²ˆμ§ΈλŠ” λ„λ‘œλ₯Ό 포μž₯ν•˜μ§€ μ•Šκ³  λ„˜μ–΄κ°€λŠ” κ²½μš°μ΄λ‹€.

λ„λ‘œλ₯Ό 포μž₯ν•˜μ§€ μ•Šμ•˜μœΌλ―€λ‘œ λ„˜μ–΄κ°€λ©΄ now.cost + next.cost 만큼의 거리 λΉ„μš©μ΄ λ°œμƒν•œλ‹€. dp 값이 ν•΄λ‹Ή 거리 λΉ„μš©λ³΄λ‹€ 크면 μ΅œμ†Ÿκ°’μœΌλ‘œ ν•΄μ£Όκ³ , μš°μ„ μˆœμœ„ 큐에 λ‹€μŒ Edge 정보λ₯Ό μΆ”κ°€ν•œλ‹€.

λ‘λ²ˆμ§ΈλŠ” λ„λ‘œλ₯Ό 포μž₯ν•˜κ³  λ„˜μ–΄κ°€λŠ” κ²½μš°μ΄λ‹€.

λ¨Όμ € λ„λ‘œλ₯Ό 포μž₯ν•  수 μžˆλŠ” 쑰건인지 ν™•μΈν•œλ‹€. 이미 포μž₯ν•œ λ„λ‘œμ˜ κ°œμˆ˜κ°€ K보닀 μž‘μœΌλ©΄(now.count < K) 포μž₯ν•  수 μžˆλ‹€.

λ„λ‘œλ₯Ό 포μž₯ν•˜λ―€λ‘œ 0의 λΉ„μš©μœΌλ‘œ λ„˜μ–΄κ°€κ³  now.cost 만큼의 거리 λΉ„μš©μ΄ λ°œμƒν•œλ‹€. dp 값보닀 μž‘μ€ λΉ„μš©μ΄λ©΄ dp 값을 μ΅œμ†Ÿκ°’μœΌλ‘œ μ—…λ°μ΄νŠΈ ν•΄μ£Όκ³ , μš°μ„ μˆœμœ„ 큐에 λ‹€μŒ Edge 정보λ₯Ό μΆ”κ°€ν•œλ‹€. λ„λ‘œλ₯Ό 포μž₯ν–ˆμœΌλ―€λ‘œ count 값은 now.count + 1이 λœλ‹€.

 

λ§ˆμ§€λ§‰μœΌλ‘œ N번 지점(포천)κΉŒμ§€ 갈 수 μžˆλŠ” μ΅œμ†Œ 거리 λΉ„μš©μ„ κ΅¬ν•œλ‹€.

long min = Long.MAX_VALUE;
for (long cost : dp[N]) {
	min = Math.min(min, cost);
}

dp[N][0] ~ dp[N][K] κ°’ μ€‘μ—μ„œ μ΅œμ†Ÿκ°’μ„ κ΅¬ν•œλ‹€.

 

μœ„ 풀이 방식을 전체 μ½”λ“œλ‘œ ν’€μ–΄λ‚΄λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

import java.util.*;
import java.io.*;

public class Main {

	static class Edge implements Comparable<Edge> {
		int end; // 도착(ν˜„μž¬) 지점
		int count; // λ„λ‘œ 포μž₯ 개수
		long cost; // 총 거리 λΉ„μš©

		Edge(int end, int count, long cost) {
			this.end = end;
			this.count = count;
			this.cost = cost;
		}

		@Override
		public int compareTo(Edge o) {
			return Long.compare(this.cost, o.cost);
		}
	}

	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		int N = Integer.parseInt(st.nextToken());
		int M = Integer.parseInt(st.nextToken());
		int K = Integer.parseInt(st.nextToken());

		List<List<Edge>> edges = new ArrayList<>(); // λ„λ‘œ 정보
		long[][] dp = new long[N + 1][K + 1]; // dp[i][k] : k개 λ„λ‘œλ₯Ό 포μž₯ν•΄μ„œ i번 μ§€μ κΉŒμ§€ κ°€λŠ” μ΅œλ‹¨ 거리 λΉ„μš©

		// edges, dp μ΄ˆκΈ°ν™”
		for (int i = 0; i <= N; i++) {
			edges.add(new ArrayList<>());
			Arrays.fill(dp[i], Long.MAX_VALUE);
		}

		for (int i = 0; i < M; i++) { // λ„λ‘œ 정보 μ €μž₯
			st = new StringTokenizer(br.readLine());
			int v1 = Integer.parseInt(st.nextToken());
			int v2 = Integer.parseInt(st.nextToken());
			int c = Integer.parseInt(st.nextToken());
			edges.get(v1).add(new Edge(v2, 0, c));
			edges.get(v2).add(new Edge(v1, 0, c));
		}

		// dijkstra
		Queue<Edge> pq = new PriorityQueue<>();
		pq.add(new Edge(1, 0, 0));
		dp[1][0] = 0;

		while (!pq.isEmpty()) {
			Edge now = pq.poll();
			if (dp[now.end][now.count] < now.cost) continue;
			for (Edge next : edges.get(now.end)) {
				if (dp[next.end][now.count] > now.cost + next.cost) { // λ„λ‘œ 포μž₯ X
					dp[next.end][now.count] = now.cost + next.cost;
					pq.add(new Edge(next.end, now.count, dp[next.end][now.count]));
				}

				if (now.count < K && dp[next.end][now.count + 1] > now.cost) { // λ„λ‘œ 포μž₯
					dp[next.end][now.count + 1] = now.cost;
					pq.add(new Edge(next.end, now.count + 1, now.cost));
				}
			}
		}

		long min = Long.MAX_VALUE;

		for (long cost : dp[N]) {
			min = Math.min(min, cost);
		}

		System.out.println(min);

	}
}

Reference

https://velog.io/@dls4585/%EB%B0%B1%EC%A4%80-1162-%EB%8F%84%EB%A1%9C%ED%8F%AC%EC%9E%A5-JAVA

 

✨ ν¬μŠ€νŒ…μ„ 마치며

λ‹€μ΅μŠ€νŠΈλΌμ— DP μœ ν˜•κΉŒμ§€ μ„žμ—¬μ„œ μ •λ‹΅ 풀이λ₯Ό 보고 μ΄ν•΄ν•˜λŠ” 데도 μ‹œκ°„μ΄ κ½€ λ“€μ—ˆλ‹€. μ°Έμ‹ ν•œ λ¬Έμ œμ˜€λ‹€κ³  μƒκ°ν•œλ‹€. 🀩

λ°˜μ‘ν˜•
LIST