forked from mmistakes/minimal-mistakes
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/rahon6000/rahon6000.githu…
- Loading branch information
Showing
10 changed files
with
402 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
--- | ||
layout: single | ||
title: "Programmers - 합승 택시 요금" | ||
date: 2023-09-19 11:26:00 +0900 | ||
toc: true | ||
toc_sticky: true | ||
tags: Java Coding-problem Graph | ||
--- | ||
Programmers 의 합승 택시 요금 문제 풀이 | ||
|
||
|
||
--- | ||
|
||
# 문제 | ||
![합승 택시 요금](https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/715ff493-d1a0-44d8-9273-a785280b3f1e/2021_kakao_taxi_01.png) | ||
지점의 개수 `n`, 출발지점을 나타내는 `s`, A의 도착지점을 나타내는 `a`, B의 도착지점을 나타내는 `b`, 지점 사이의 예상 택시요금을 나타내는 `fares`가 매개변수로 주어집니다. 이때, A, B 두 사람이 s에서 출발해서 각각의 도착 지점까지 택시를 타고 간다고 가정할 때, 최저 예상 택시요금을 계산해서 return 하도록 solution 함수를 완성해 주세요. | ||
만약, 아예 합승을 하지 않고 각자 이동하는 경우의 예상 택시요금이 더 낮다면, 합승을 하지 않아도 됩니다. | ||
- 지점갯수 n은 3 이상 200 이하인 자연수입니다. | ||
- 지점 s, a, b는 1 이상 n 이하인 자연수이며, 각기 서로 다른 값입니다. | ||
- 즉, 출발지점, A의 도착지점, B의 도착지점은 서로 겹치지 않습니다. | ||
- fares는 2차원 정수 배열입니다. | ||
- fares 배열의 크기는 2 이상 n x (n-1) / 2 이하입니다. | ||
- 예를들어, n = 6이라면 fares 배열의 크기는 2 이상 15 이하입니다. (6 x 5 / 2 = 15) | ||
- fares 배열의 각 행은 [c, d, f] 형태입니다. | ||
- c지점과 d지점 사이의 예상 택시요금이 f원이라는 뜻입니다. | ||
- 지점 c, d는 1 이상 n 이하인 자연수이며, 각기 서로 다른 값입니다. | ||
- 요금 f는 1 이상 100,000 이하인 자연수입니다. | ||
- fares 배열에 두 지점 간 예상 택시요금은 1개만 주어집니다. 즉, [c, d, f]가 있다면 [d, c, f]는 주어지지 않습니다. | ||
- 출발지점 s에서 도착지점 a와 b로 가는 경로가 존재하는 경우만 입력으로 주어집니다. | ||
|
||
# 해결 | ||
그래프 관련 구현은 경험이 거의 없어서 푸는 데 시간이 꽤 걸렸습니다. 다행히 예전에 유니티로 A* algorithm 구현해 본 기억하고, 대학원생 때 후배 S 가 했던 발표 내용 중에 Dijkstra algorithm 이 기억에 있었습니다. ~~S 가 발표를 엄청 잘하는 친구여서 기억에 남아있었다~~ | ||
|
||
두 가지 시행 착오가 있었습니다. | ||
- 체크해야 할 노드를 어떻게 관리해야 하는지 몰랐어서, 코드도 복잡해지고 상당히 헤멨습니다. Queue 를 쓰면 딱 좋은 상황이란 걸 뒤늦게 알았습니다. | ||
- 노드의 초기 cost 를 설정할 때, 적당히 높은 값을 줘야 했는데 너무 작게 줘서 몇몇 case 에서 실패한 적이 있습니다. | ||
- 저 같은 경우엔 `int cost = Integer.MAX_VALUE;` 로 해결했습니다. | ||
- 문제 조건에서 max 조건인 `n=200`, `f=100,000` 을 잘 생각해보면 최소한 둘을 곱한 값을 써야 한다는 걸 알 수 있습니다. | ||
- 아니면 `int cost = -1;` 같이 사용되지 않는 범위에 적당히 두고, `setCost()` 메서드에서 해당 경우를 처리해 줘도 되었겠습니다. | ||
|
||
아래 코드는 programmer에 제출한 것이 아니라, 다른 에디터에서 작성해 둔 것입니다. | ||
```java | ||
import java.util.*; | ||
|
||
public class Programmers { | ||
public static void main(String[] args) { | ||
int print = solution( | ||
6, 4, 6, 2, | ||
new int[][] { | ||
{4, 1, 10}, {3, 5, 24}, {5, 6, 2}, | ||
{3, 1, 41}, {5, 1, 24}, {4, 6, 50}, | ||
{2, 4, 66}, {2, 3, 22}, {1, 6, 25}} | ||
); | ||
|
||
System.out.println(print); | ||
} | ||
|
||
// 여기부터 제출 | ||
public static int solution(int n, int s, int a, int b, int[][] fares) { | ||
ArrayList<Node> graph = initialize(n, fares); // initialize 도 dijkstra 안에 넣는게 나았겠네요. | ||
int[] resultN = dijkstra(graph, n, s); | ||
graph = initialize(n, fares); | ||
int[] resultA = dijkstra(graph, n, a); | ||
graph = initialize(n, fares); | ||
int[] resultB = dijkstra(graph, n, b); | ||
|
||
int answer = Integer.MAX_VALUE; | ||
for(int i = 0; i < n; i++) { | ||
int trial = resultN[i] + resultA[i] + resultB[i]; | ||
if(trial == 0) continue; | ||
answer = (answer > trial)? trial:answer; | ||
} | ||
return answer; | ||
} | ||
|
||
private static int[] dijkstra(ArrayList<Node> graph, int n, int s) { | ||
int[] result = new int[n]; | ||
Node start = graph.get(s-1); | ||
start.cost = 0; | ||
LinkedList<Node> que = new LinkedList<Node>(); // Queue 로 처리해야 할 노드를 관리 | ||
que.add(start); | ||
while(!que.isEmpty()) { | ||
Node checkNode = que.pollFirst(); | ||
ArrayList<Node> neig = checkNode.neigbours; | ||
int neigSize= neig.size(); | ||
for(int i = 0; i < neigSize; i++) { | ||
if(neig.get(i).setCost(checkNode.cost + | ||
checkNode.weights.get(i))) { | ||
que.add(neig.get(i)); // 노드의 cost 를 갱신했다면, 그 노드의 이웃 노드는 다시 계산해 볼 필요가 생깁니다. | ||
result[neig.get(i).idx-1] = checkNode.cost + | ||
checkNode.weights.get(i); | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
private static ArrayList<Node> initialize(int n, int[][] fares) { | ||
ArrayList<Node> list = new ArrayList<Node>(); | ||
for(int i = 0; i<n; i++) { | ||
list.add(new Node(i+1)); | ||
} | ||
for(int[] i : fares) { | ||
list.get(i[0]-1).setGraph(list.get(i[1]-1), i[2]); | ||
} | ||
return list; | ||
} | ||
} | ||
|
||
class Node{ | ||
int idx; | ||
int cost = Integer.MAX_VALUE; // 이 값이 너무 낮게 설정되어 실패 케이스가 있었다... | ||
// 생각해보면 요금이 100,000 이 최대고 n이 200 이하니까 최소한 둘 곱한 것은 되어야 했음. | ||
// 이런 스케일을 미리 생각할 수 있으면 도움 될듯... | ||
ArrayList<Node> neigbours = new ArrayList<>(); | ||
ArrayList<Integer> weights = new ArrayList<>(); | ||
Node(int idx){ | ||
this.idx = idx; | ||
} | ||
boolean setCost(int cost) { | ||
if(this.cost > cost) { // 사실 이 부분이 dijkstra algorithm 의 일부라서, 여기 있는게 그리 좋아보이진 않네요. | ||
this.cost = cost; | ||
return true; | ||
} | ||
return false; | ||
} | ||
void setGraph(Node other, int weight) { | ||
this.neigbours.add(other); | ||
other.neigbours.add(this); | ||
this.weights.add(weight); | ||
other.weights.add(weight); | ||
} | ||
@Override | ||
public String toString() { // 디버깅용 메서드라 실제 제출할 땐 뺐습니다. | ||
return "Node:" + idx + " Cost:" + cost + "\n"; | ||
} | ||
} | ||
``` | ||
|
Oops, something went wrong.