const fs = require('fs');
const stdin = fs.readFileSync('/dev/stdin').toString().trim().split("\n");
class MinHeap {
constructor() {
this.heap = [];
}
size() {
return this.heap.length;
}
getLeftChildIndex(parentIndex) {
return 2 * parentIndex + 1;
}
getRightChildIndex(parentIndex) {
return 2 * parentIndex + 2;
}
getParentIndex(childIndex) {
return Math.floor((childIndex - 1) / 2);
}
swap(index1, index2) {
const temp = this.heap[index1];
this.heap[index1] = this.heap[index2];
this.heap[index2] = temp;
}
peek() {
if (this.heap.length === 0) {
return null;
}
return this.heap[0];
}
insert(value) {
this.heap.push(value);
this.heapifyUp();
}
heapifyUp() {
let currentIndex = this.heap.length - 1;
while (currentIndex > 0) {
const parentIndex = this.getParentIndex(currentIndex);
if (this.heap[parentIndex][1] > this.heap[currentIndex][1]) {
this.swap(parentIndex, currentIndex);
currentIndex = parentIndex;
} else {
break;
}
}
}
remove() {
if (this.heap.length === 0) {
return null;
}
if (this.heap.length === 1) {
return this.heap.pop();
}
const removedValue = this.heap[0];
this.heap[0] = this.heap.pop();
this.heapifyDown();
return removedValue;
}
heapifyDown() {
let currentIndex = 0;
while (this.getLeftChildIndex(currentIndex) < this.heap.length) {
const leftChildIndex = this.getLeftChildIndex(currentIndex);
const rightChildIndex = this.getRightChildIndex(currentIndex);
const smallerChildIndex =
rightChildIndex < this.heap.length &&
this.heap[rightChildIndex][1] < this.heap[leftChildIndex][1]
? rightChildIndex
: leftChildIndex;
if (this.heap[currentIndex][1] > this.heap[smallerChildIndex][1]) {
this.swap(currentIndex, smallerChildIndex);
currentIndex = smallerChildIndex;
} else {
break;
}
}
}
}
// 최소 힙 구현부
const [V, E, start, end, money] = stdin[0].split(" ").map((ele) => +ele);
const edgeInfo = stdin.slice(1).map((ele) => ele.split(" ").map((ele) => +ele));
// 입력
const adjacencyList = Array.from({ length: V + 1 }, () => []);
const costs = [];
const heap = new MinHeap();
edgeInfo.forEach(([from, to, cost]) => {
adjacencyList[from].push([to, cost]);
adjacencyList[to].push([from, cost]);
costs.push(cost);
});// 간선 정보 -> 인접 리스트 저장
const dijkstra = (maximum) => {
const dist = new Array(V + 1).fill(Infinity);
dist[start] = 0;
heap.insert([start, 0]);
// 이후 힙에 해당 노드에 도달하기 까지의 최소비용을 기준으로 최소 힙을 구성
while (heap.size() !== 0) {
const [crntVertex, crntCost] = heap.remove();
// 힙에 남아 있는 요소 중 우선순위가 제일 높은 요소(해당 노드까지 도달하는 최소 비용으로 업데이트 된 값) 꺼내옴
if (dist[crntVertex] != crntCost) continue;
// 거리에 저장되어 있는 값이 최소 비용이 아니라면 스킵
for (let [nextVertex, nextCost] of adjacencyList[crntVertex]) {
const totalCostToNextVertex = crntCost + nextCost;
// 현재 노드에서 갈 수 있는 후보군 중 정점까지의 비용
if (nextCost <= maximum && totalCostToNextVertex < dist[nextVertex]) {
// 다음 노드 까지의 비용이 만약 가정하는 총 자금(maximum)보다 작아 감당 가능
// && 이전 while문 순회 회차에 이미 저장되어있는 다음 노드 까지의 비용(dist[nextVertex])이 작다면
// => dist[nextVertex] 갱신 및 힙에 다음 노드와 비용을 저장
dist[nextVertex] = totalCostToNextVertex;
heap.insert([nextVertex, totalCostToNextVertex]);
}
}
}
return dist[end] <= money;
// 통행비의 최대(maximum)을 제한 했을 때 구성 가능한 원하는 목적지까지의 총 비용이
// 입력에서 주어진 예산보다 작은가 여부를 리턴
};
costs.sort((a, b) => a - b);
// 간선들이 가질 수 있는 비용들을 이진탐색하기 위한 정렬
let [lt, rt] = [0, costs.length - 1];
let answer = Infinity;
while (lt <= rt) {
const mid = Math.floor((lt + rt) / 2);
if (dijkstra(costs[mid])) {
if (costs[mid] < answer) {
answer = costs[mid];
}
rt = mid - 1;
} else {
lt = mid + 1;
}
}
console.log(answer === Infinity ? -1 : answer);
→ 다익스트라 알고리즘을 사용하는 데 BFS 수행(우선순위 큐를 사용해 BFS이라고 해도 될지 모르겠습니다.)을 우선순위 큐를 이용했고
→ 다익스트라 알고리즘을 사용하긴 하지만 DP 배열을 해당 목적지까지의 최소비용만을 조건으로 갱신하는 것이 아닌 지날 수 있는 간선의 최대 값을 조건
이후 이분 탐색을 하는데 매개변수 탐색(parametric search)를 사용한다.
💡 매개변수 탐색(parametric search)의 조건
- 특정 조건을 만족하는 최댓값/최솟값을 구하는 형식의 문제여야 한다.
→ 노드 탐색 배용이 총 예산 보다 작거나 같은 조건을 만족하는 각 골목들의 통행료들 중 최솟값- 최댓값을 구하는 문제의 경우 어떤 값이 조건을 만족하면 그 값보다 작은 값은 모두 조건을 만족해야 한다.(최솟값의 경우 그 값보다 큰 값은 모두 조건을 만족해야 한다.)
→ 이것도 바이너리 서치를 하기 위한 조건으로 어떤 기점으로 거짓이 되는 구간, 참이되는 구간이 있어야 미드 값을 조절할 수 있기 때문에…- 답의 범위가 이산적이거나(e.g. 정수) 허용 오차 범위가 있어야 한다.
→ 바이너리 서치를 해야하기 때문에…, ****1 ≤ 골목 별 수금액 ≤ 10^9
조건들의 출처
골목별 비용들의 후보값들을 다익스트라 알고리즘을 수행하는 함수의 인자로 준다.