2 분 소요

등산코스정하기 (프로그래머스 Level 3)

https://school.programmers.co.kr/learn/courses/30/lessons/118669

조금 특이한 방식의 길찾기 문제이다
문제가 조금 복잡하게 설명되어 있지만
요지는 ‘시작점에서 목적지까지 가는 중, 단일 길의 비용이 적은 것을 고르는 문제’이다
(누적 비용은 고려하지 않는다)

문제에서 유의해야 할 점은
‘등산로 입구’가 처음과 끝의 한 번, ‘산봉우리’는 한 번만 포함
이라는 설명이 있는데
‘등산로 입구’와 ‘산봉우리’를 제외한 나머지 위치는 ‘중복방문’이 허용된다
따라서
‘등산로 입구 -> 산봉우리’의 경로만 신경을 쓰면 되는 길찾기 문제이다
(다만 처음에는 ‘중복 방문’과 ‘단일 길의 비용이 적은 것만을 고려’ 라는 측면에 있어
최단거리 길찾기 보다 DFS를 먼저 떠올려 진행하였다)

처음에는 DFS(백트래킹) + DP 의 조합으로 풀려 하였으나
DFS의 특성상 모든 루트를 탐색하기에 DP를 사용하더라도 여전히 느렸으며,
일부 테스트 케이스를 통과할 수 없었기에
(아마 특정 ‘등산로 입구’에서 먼저 DFS를 돌게 되어 차후 비용 비교에 영향을 준 것으로 추정)

따라서 우선순위 큐를 사용하는 다익스트라를 통하여
가능한 ‘경로의 비용이 낮은’ 노드를 우선하여 탐색하도록 하는 방식으로 풀 수 있었다
(추가적으로 더 좋은 경우의 수가 방문될 수 있으니 목적지에 도착하더라도 바로 break를 하지 않았다)

DFS와 BFS에 대하여 익숙해진 듯 하면서도
이번 문제가 ‘최단 거리’와 관련이 있다고 생각하지 못하여
삽질을 하였다

Code

#include <vector>
#include<unordered_map>
#include<unordered_set>
#include<algorithm>
#include<limits.h>
#include<queue>

using namespace std;

struct Edge
{
	int target;
	int cost;
};

vector<int> solution(int n, vector<vector<int>> paths, vector<int> gates, vector<int> summits) {
	vector<int> answer;
	vector<int> dp(n + 1, INT_MAX);
	answer.push_back(INT_MAX);
	answer.push_back(INT_MAX);

	sort(gates.begin(), gates.end());
	sort(summits.begin(), summits.end());

	unordered_map<int, vector<Edge>> m;
	for (const auto& path : paths)
	{
		int start = path[0];
		int end = path[1];
		int cost = path[2];

		if (m.find(start) == m.end())
			m[start] = vector<Edge>();

		m[start].push_back({ end,cost });

		if (m.find(end) == m.end())
			m[end] = vector<Edge>();

		m[end].push_back({ start,cost });
	}

	// 비용 순으로 정렬시키기
	for (auto& p : m)
	{
		sort(p.second.begin(), p.second.end(), [](const Edge& a, const Edge& b) {
			if (a.cost == b.cost)
				return a.target < b.target;
			return a.cost < b.cost;
			});
	}

	unordered_set<int> gs(gates.begin(), gates.end());
	unordered_set<int> ss(summits.begin(), summits.end());

	struct node
	{
		int end, cost;
	};

	struct Compare
	{
		bool operator() (const node& a, const node& b)
		{
			if (a.cost == b.cost)
				return a.end > b.end;

			return a.cost > b.cost;
		}
	};

	priority_queue<node, vector<node>, Compare> pq;

	for (int g : gates)
	{
		dp[g] = 0;
		pq.push({g,0});
	}

	vector<bool> visited(n + 1, false);

	while (pq.empty() == false)
	{
		auto n = pq.top();
		pq.pop();

		// 이미 더 좋은 상황으로 방문됨
		if (n.cost > dp[n.end])
			continue;

		if (visited[n.end] == true)
			continue;

		dp[n.end] = n.cost;
		visited[n.end] = true;

		// 도착
		if (ss.find(n.end) != ss.end())
		{
			if (answer[1] > n.cost || (answer[1] == n.cost && answer[0] > n.end))
			{
				answer[0] = n.end;
				answer[1] = n.cost;
			}

			visited[n.end] = false;
			continue;
		}

		for (const Edge& v : m[n.end])
		{
			int cost = max( n.cost,v.cost);
			pq.push({ v.target,cost });
		}
	}

	return answer;
}

댓글남기기