프로그래머스 - 합승 택시 요금 (Lv.3)

4 minute read

문제 링크

문제 설명

  • N개의 정점이 있고, 각 정점은 서로 가중치를 가진 간선으로 연결된 그래프 형태를 취하고 있다.
  • 출발지점(S)와 두 사람의 목적지(A,B)가 주어진다.
  • 두 사람이 서로의 목적지에 가는 방법은 다음과 같다.
    • 합승을 통해 중간 지점까지 같이 이동한다.
    • 중간 지점에서 서로의 목적지로 각자 이동한다.
    • 합승을 하지 않고 처음부터 각자 이동할 수 있다.

문제 접근1

  • 다익스트라 알고리즘
    • 음의 가중치가 없는 그래프의 한 정점에서 다른 모든 정점까지의 최단 거리를 구하는 알고리즘
  • 합승해서 갈 수 있는 모든 경로를 DFS 를 이용해 구한다.
  • 합승을 통해 이동한 중간 지점에서 다익스트라 알고리즘으로 서로의 목적지까지 가는 최소 비용을 계산한다.
  • 위와 같은 방식으로 완전 탐색을 하고, 최소가 되는 값을 반환한다.

구현(정확성 All Pass, 효율성 All Fail)

#include <string>
#include <vector>
#include <iostream>
#include <queue>
#include <memory.h>
#include <algorithm>

#define INF 9e8

using namespace std;
// w = table[u].first, v = table[u].second
vector<pair<int, int>> table[201];
int cand[201][201]; // cand[v][u] 는 v가 u에서 왔을 때 비용
int answer = INF;
int N, A, B;
int cost[201];
bool visited[201];

void dijkstra(int start, int curCost){
    for(int i=0; i<=N; i++){
        cost[i]=INF;
    }
    cost[start] = 0;
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push(make_pair(0, start));
    
    while(!pq.empty()){
        int u = pq.top().second;
        int w = pq.top().first;
        pq.pop();
        
        for(int i=0; i<table[u].size(); i++){
            int v = table[u][i].second;
            int x = table[u][i].first;
            if(w+x < cost[v]){
                cost[v] = w+x;
                pq.push(make_pair(w+x, v));
            }
        }
    }
    
    // 합승지점까지의 비용 + (A, B 지점까지의 비용)
    int totalCost = curCost + cost[A] + cost[B];
    answer = min(answer, totalCost);
    // printf("%d -> %d(a) : %d\n", start, A, cost[A]);
    // printf("%d -> %d(b) : %d\n", start, B, cost[B]);
    // printf("current cost: %d\n", curCost);
    // printf("total cost: %d\n",answer);
}

void taxiProcess(int u, int weight){
    for(int i=0; i<table[u].size(); i++){
        int v = table[u][i].second;
        int w = table[u][i].first;
        if(visited[v]) continue;
        visited[v] = true;
        dijkstra(v, weight+w);
        taxiProcess(v, weight+w);
        visited[v] = false;
    }
}

int solution(int n, int s, int a, int b, vector<vector<int>> fares) {
    
    N = n; A = a; B = b;
    for(int i=0; i<fares.size(); i++){
        int u = fares[i][0];
        int v = fares[i][1];
        int w = fares[i][2];
        table[u].push_back(make_pair(w, v));
        table[v].push_back(make_pair(w, u));
    }
    memset(visited, false, sizeof(visited));
    dijkstra(s, 0); // 합승하지 않는 경우
    taxiProcess(s, 0);
    
    return answer;
}

문제 접근 2

  • 다익스트라 알고리즘을 아래와 같이 적용한다.
    • (start -> mid 까지 비용) + (mid->a 까지 비용) + (mid->b 까지 비용) 을 구해서 최솟값을 찾는다.

구현(효율성 7,8번 Fail)

#include <vector>
#include <iostream>
#include <queue>
#include <algorithm>

#define INF 1000000

using namespace std;
// w = table[u].first, v = table[u].second
int N;
vector<pair<int, int>> table[201];
int cost[201];

int dijkstra(int start, int target){
    for(int i=1; i<=N; i++){
        cost[i]=INF;
    }
    cost[start] = 0;
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push({0, start});
    
    while(!pq.empty()){
        int u = pq.top().second;
        int w = pq.top().first;
        pq.pop();
        if(cost[u] < w) continue;
        for(int i=0; i<table[u].size(); i++){
            int v = table[u][i].second;
            int x = table[u][i].first;
            if(w+x < cost[v]){
                cost[v] = w+x;
                pq.push(make_pair(w+x, v));
            }
        }
    }
    return cost[target];
}

int solution(int n, int s, int a, int b, vector<vector<int>> fares) {
    int ret = INF;
    N = n;
    for(int i=0; i<fares.size(); i++){
        int u = fares[i][0];
        int v = fares[i][1];
        int w = fares[i][2];
        table[u].push_back(make_pair(w, v));
        table[v].push_back(make_pair(w, u));
    }
    // 합승하지 않는 경우
    ret = dijkstra(s, a) + dijkstra(s,b);
    for(int i=1; i<=n; i++){
        if(s!=i) 
            ret = min(ret, dijkstra(s,i) + dijkstra(i, a) + dijkstra(i, b));
    }
    return ret;
}

보완 내용

  • 2번째 풀이는 다익스트라 알고리즘을 총 3xN + 2 번 해야한다.
  • 다익스트라 알고리즘을 시작점 s에서 모든 점에 대해서 1번, a에서 모든 점에 대해서 1번, b에서 모든 점에 대해서 1번으로 총 3번만 진행할 수 있다.
    • 2차원 배열의 형태로 0행은 시작점 s에서 모든 노드로 가는 최소비용을 담는다.
    • 1행은 a에서 모든 노드로 가는 최소비용을 담는다.
    • 2행은 b에서 모든 노드로 가는 최소비용을 담는다.
    • 무방향 그래프이므로 출발지와 도착지의 관계는 중요하지 않기 때문에 가능한 풀이이다.
      • 방향 그래프였다면 2차원 배열에 담긴 의미가 달라진다. 즉, s에서 1번 노드로 가는 최소비용과 1번 노드에서 s로 가는 최소비용은 달라질 수 있다.
      • 방향 그래프일 때 이런 방법을 사용할 수 없는 이유는 시작점->중간점 은 배열의 정의대로 가지만, 중간점->A 혹은 B로 가는 경로는 출발점이 중간점이기 때문이다.

구현(All Pass)

#include <vector>
#include <iostream>
#include <queue>
#include <algorithm>

#define INF 1000000

using namespace std;
// w = table[u].first, v = table[u].second
int N;
vector<pair<int, int>> table[201];
int cost[201];
// s(0), a(1), b(2)
// dList[0][1]~dList[0][200] 은 s부터 각 노드까지의 최소비용
int dList[3][201]; 

void dijkstra(int idx, int start){
    for(int i=1; i<=N; i++){
        dList[idx][i]=INF;
    }
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push({0, start});
    dList[idx][start]=0;
    
    while(!pq.empty()){
        int u = pq.top().second;
        int w = pq.top().first;
        pq.pop();
        if(dList[idx][u] < w) continue;
        for(int i=0; i<table[u].size(); i++){
            int v = table[u][i].second;
            int x = table[u][i].first;
            if(w+x < dList[idx][v]){
                dList[idx][v] = w+x;
                pq.push(make_pair(w+x, v));
            }
        }
    }
}

int solution(int n, int s, int a, int b, vector<vector<int>> fares) {
    int ret = INF;
    N = n;
    for(int i=0; i<fares.size(); i++){
        int u = fares[i][0];
        int v = fares[i][1];
        int w = fares[i][2];
        table[u].push_back(make_pair(w, v));
        table[v].push_back(make_pair(w, u));
    }
    dijkstra(0, s);
    dijkstra(1, a);
    dijkstra(2, b);
    ret = dList[1][s] + dList[2][s];
    for(int i=1; i<=n; i++){
        if(dList[0][i]==INF || dList[1][i]==INF || dList[2][i]==INF) continue;
        ret = min(ret, dList[0][i] + dList[1][i] + dList[2][i]);
    }
    
    return ret;
}

Leave a comment