解題想法

這題用的演算法主要是dijkstra,求權重圖的最短路徑,但不能有負邊。

由於題目給的是到終點時的貨物數量,要我們求起點出發要有多少貨物。 所以在這邊我們選擇從終點反向計算到各點的single-source shortest path。最後輸出終點到起點的shortest path。

這題邊的權重可以看做是過路費,進到鄉村只收1單位貨物,進到城市則是每20個貨物收1單位貨物的過路費。

因此dijkstra在進行relaxation的時候,要看該點權重加上進入該點的過路費,有沒有小於下個點權重,小於的話就更新下個點的shortest path。

上圖由A點更新其他點,更新後各點的狀況。

另外,題目還需要輸出經過的地方。這個只需要用一個陣列pre[]來記錄他的上個點是誰即可。

參考

我在學dijkstra的時候,看的是這位印度帥哥的教學影片。大推! Dijkstra - Abdul Bari

程式碼

#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
// 將abc轉為0~51的編碼
int decode(char x)
{
    if (x >= 65 && x <= 90)
        return x-65;
    if (x >= 97 && x <= 122)
        return x-71;
    return 0;
}
// 將0~51的編碼轉回abc
int encode(int x)
{
    if (x >= 0 && x <= 25)
        return x + 65;
    if (x >= 26 && x <= 51)
        return x + 71;
    return 0;
}

long long INF = 0x7fffffffffff;     // 無限大
int cases = 1;  // 紀錄目前為第幾個case

int main()
{
    int path_num;
    char a, b, s, e;
    int aa, bb, start, end, item_num;
    while (cin >> path_num && path_num != -1) {
        vector<int> map[60];
        for (int i = 0; i < path_num; i++) {
            cin >> a >> b;
            aa = decode(a);
            bb = decode(b);
            // 建圖
            map[aa].push_back(bb);
            map[bb].push_back(aa);
        }
        cin >> item_num >> s >> e;    // 輸入題目問題
        start = decode(s);
        end = decode(e);
        // 由end開始進行反向dijkstra
        int pre[60];        // 該點的前一個點 
        bool visit[60];     // 是否造訪過
        long long d[60];    // 該點的最短路徑值
        // 初始化
        for (int i = 0; i < 60; i++) {
            pre[i] = -1;
            visit[i] = false;
            d[i] = INF;
        }

        queue<int> q;
        q.push(end);
        visit[end] = true;
        d[end] = item_num;

        int front, current, smaller_no;
        long long toll, smaller;
        while (!q.empty()) {
            front = q.front();
            q.pop();
            // 計算過路費
            if (front >= 0 && front <= 25)  // 城市
                toll = ceil(d[front] * 20.0 / 19.0);
            else    // 鄉村
                toll = d[front] + 1;
            // 進行dijkstra relaxation
            for (int i = 0; i < map[front].size(); i++) {
                current = map[front][i];
                if (toll < d[current]) {
                    d[current] = toll;
                    pre[current] = front;
                }
            }
            // 挑選目前圖中,未造訪且權重最小的點
            smaller = INF;
            for (int i = 0; i < 52; i++)
                if (visit[i] == false && d[i] < smaller) {
                    smaller = d[i];
                    smaller_no = i;
                }
            // 將點放到queue裡面
            if (smaller < INF) {
                q.push(smaller_no);
                visit[smaller_no] = true;
            }
        }
        // 輸出答案
        cout << "Case " << cases++ << ":" << endl;
        cout << d[start] << endl;
        current = start;
        while (current != end) {
            cout << (char)encode(current) << "-";
            current = pre[current];
        }
        cout << (char)encode(current) << endl;
    }
    return 0;
}