【PAT算法之路】 -- 最短路徑 1030 Travel Plan (30 分) C++解法

【PAT算法之路】 – 專欄總攬

最短路徑幾乎是PAT出現頻率最高的題型之一,一般都需要70-100行代碼(C++),但是不用慌,因爲這個代碼有40行以上都是靠背下來的(都是套路)。
我個人記的是Dijkstra+DFS,這種方法感覺比較通用,而且容易記住。

我們用一個例子來看看吧,就比如: 1030 Travel Plan (30 分)

1030 Travel Plan (30 分)

A traveler’s map gives the distances between cities along the highways, together with the cost of each highway. Now you are supposed to write a program to help a traveler to decide the shortest path between his/her starting city and the destination. If such a shortest path is not unique, you are supposed to output the one with the minimum cost, which is guaranteed to be unique.

Input Specification:

Each input file contains one test case. Each case starts with a line containing 4 positive integers N, M, S, and D, where N (≤500) is the number of cities (and hence the cities are numbered from 0 to N−1); M is the number of highways; S and D are the starting and the destination cities, respectively. Then M lines follow, each provides the information of a highway, in the format:

City1 City2 Distance Cost

where the numbers are all integers no more than 500, and are separated by a space.

Output Specification:

For each test case, print in one line the cities along the shortest path from the starting point to the destination, followed by the total distance and the total cost of the path. The numbers must be separated by a space and there must be no extra space at the end of output.

Sample Input:

4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

Sample Output:

0 2 3 3 40

題目意思很簡單就是從一個指定的點出發,到指定的終點,求最短路徑,如果最短路徑不止一條,選擇Cost最小的那一條。

容我先給出代碼:

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

using namespace std;

#define INF 99999999
int e[510][510];
int e2[510][510];
int dis[510];
bool vis[510];
vector<int> pre[510];

int n,m,s,d;
int min_cost = INF;
vector<int> path;

void dfs(int c,vector<int> temp_path){
    temp_path.push_back(c);
    if(c == s){
        int sum_cost = 0;
        for(int i=1;i<temp_path.size();i++)
            sum_cost += e2[temp_path[i]][temp_path[i-1]];
        if(min_cost > sum_cost){
            min_cost = sum_cost;
            path = temp_path;
        }
        return;
    }
    int len = pre[c].size();
    for(int i=0;i<len;i++){
        dfs(pre[c][i],temp_path);
    }
    temp_path.pop_back();
}

int main() {
    fill(e[0],e[0]+510*510,INF);
    fill(e2[0],e2[0]+510*510,INF);
    fill(dis,dis+510,INF);

    cin >> n >> m >> s >> d;
    for(int i=0;i<m;i++){
        int c1,c2,diss,cost;
        cin >> c1 >> c2 >> diss >> cost;
        e[c1][c2] = diss;
        e[c2][c1] = diss;
        e2[c1][c2] = cost;
        e2[c2][c1] = cost;
    }

    dis[s] = 0;
    for(int i=0;i<n;i++){
        int min_ = INF,min_j = -1;
        for(int j=0;j<n;j++)
            if(!vis[j] && min_ > dis[j]){
                min_ = dis[j];
                min_j = j;
            }

        if(min_j == -1) break;
        vis[min_j] = true;

        for(int j=0;j<n;j++){
            if(dis[j] > e[min_j][j]+dis[min_j]){
                dis[j] = e[min_j][j]+dis[min_j];
                pre[j].clear();
                pre[j].push_back(min_j);
            }
            else if(dis[j] == e[min_j][j]+dis[min_j])
                pre[j].push_back(min_j);
        }

    }

//    for(auto item:pre[d])
//        cout << item << "\t";

    vector<int> temp_vec;
    dfs(d,temp_vec);

    reverse(path.begin(),path.end());
    for(auto item:path)
        cout << item << " ";

    cout << dis[d] << " ";
    cout << min_cost;
    return 0;
}

一、定義和初始化

首先對於最短路徑的題,選擇e[][]來存圖。大小就是題目給的最大結點數+一點點。

然後就需要定義下面這些的變量

int dis[510];				// 存儲起點到每個點的最短距離
bool vis[510];			// 存儲是否訪問某個節點

vector<int> pre[510];		// 存儲前置節點
// (如果只有Dijkstra,用不上這個,這個是連接Dijkstra和DFS的橋樑)

首先要初始化vis[]全爲false,還未訪問(定義的時候已經自動初始化了),還有就是dis[]、e[][]全爲INF。

還有就是正確的讀入數據,保存到e[][]中了,有些題有點權,那麼就需要定義一個weight[]保存點的權重。

30行代碼寫出來了。。。/w\

二、Dijkstra算法

這個在理解的基礎上直接背:

dis[起點]爲0

循環n(結點數)次, 在循環體中選擇未訪問的,最小dis[]的結點

根據選擇的結點更新各個結點的dis[]

如果需要保存前置結點,則在更新dis[]時,更新pre

具體看上面的代碼。

再加上20行代碼。。。

二.五、檢查pre

這個很重要,因爲pre馬上要用於DFS了,我們可以在寫DFS前檢查下,方法很簡單

//    for(auto item:pre[d])
//        cout << item << "\t"

不出意外會打印結點d的前置結點。如果出了意外,回去仔細檢查Dijkstra有沒有那裏寫錯了。

三、DFS算法

如果只求點到點的最短距離,那麼是不需要DFS,出現DFS一般就是點到點的最短距離不unique,需要根據點權或者像上面這道題的邊的其他權重來確定唯一的路徑。(都是套路)

但是DFS還是需要大家對遞歸和回溯有比較好的掌握的,比如記得pop_back什麼的。比較統一的寫法是,遞歸結束條件一般都是回到了起點。在這個時候根據遞歸參數保存的值來更新結果。

具體看上面的代碼。

四、打印結果

這個就自己寫了。。。

總結,作者水平有限,有哪裏有問題都歡迎評論指出。如果對你有幫助歡迎點贊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章