**PAT_甲級_1131 Subway Map (30point(s)) (C++)【BFS求最短路徑/DFS篩選最終結果】

目錄

1,題目描述

題目大意

2,思路

數據結構

算法

說明

3,AC代碼

4,解題過程


1,題目描述

Sample Input:

4
7 1001 3212 1003 1204 1005 1306 7797
9 9988 2333 1204 2006 2005 2004 2003 2302 2001
13 3011 3812 3013 3001 1306 3003 2333 3066 3212 3008 2302 3010 3011
4 6666 8432 4011 1306
3
3011 3013
6666 2001
2004 3001

 

Sample Output:

2
Take Line#3 from 3011 to 3013.
10
Take Line#4 from 6666 to 1306.
Take Line#3 from 1306 to 2302.
Take Line#2 from 2302 to 2001.
6
Take Line#2 from 2004 to 1204.
Take Line#1 from 1204 to 1306.
Take Line#3 from 1306 to 3001.

題目大意

給出幾條地鐵線路,以及若干對起點和終點,要求輸出起點到終點的最短路徑(路過的站點數目最少,若相同,則選擇換乘次數最少的)。

 

2,思路

參考大神的解法@日沉雲起【pat甲級1131 Subway Map (30 point(s))】

數據結構

  • int dis[10005]:存放起點到各個站點的最短距離(即最少經過的站點數目);
  • unordered_map<int, int> line:key:一段路(每段路有兩個端點v1,v2,用一個整型變量v1*10000+v2表示v1->v2,v2*10000+v1表示v2->v1) 。value:屬於幾號線(每段路只屬於一條線);
  • vector<int> graph[10005]:圖的鄰接表表示法,與i站點相鄰的站點編號存放至graph[i]對應的vector中;
  • vector<int> pre[10005]:存放起點到所有節點的最短路徑(每個節點的前驅節點)。準確來說並不是所有節點,一旦距離超過dis[dest]便不再繼續計算後面的節點;
  • vector<int> path:存放最短路徑中的所有節點;
  • vector<pair<int, int>> trans:first換乘站編號、second"換乘站->下一站"所屬線路;

算法

  1. 接受數據構建無向鄰接表圖,並記錄每段路(int型變量表示,具體見數據結構中描述)所屬線路:
  2. 每對查詢的始終點使用一次BFS求出起點到終點的所有最短路徑,存入pre中,最短路徑的具體值存放至dis:
  3. 多所有的最短路徑使用一次DFS,求出最終答案path。遍歷path用trans記錄換乘節點及路線:

說明

trans:

 

 

3,AC代碼

#include<bits/stdc++.h>
using namespace std;
int N, M, start, dest, dis[10005];          //N線路數目 M查詢數目 start查詢起點 dest目的地
unordered_map<int, int> line;               //key:一段路 value:屬於幾號線(每段路只屬於一條線)
vector<int> graph[10005], pre[10005], path; //graph圖、pre最短路徑中每個節點的前驅節點、path最終路徑(含每個節點)
vector<pair<int, int>> trans;               //換乘站編號、"換乘站->下一站"所屬線路

void dfs(int stop){                         //因爲最短路徑記錄的是前驅節點 所以遍歷時需要從終點向起點遍歷
    path.push_back(stop);
    if(stop == start){                      //出口條件
        vector<pair<int, int>> tempTrans;
        tempTrans.push_back({start, -1});   //存放起點 沒有任何站點到達起點 所以將線路設爲-1
        for(int i = path.size()-2; i > 0; i--){ //path存放的最短路徑是從終點到起點的 所以從後向前遍歷
            if(line[path[i]*10000 + path[i-1]] != line[path[i]*10000 + path[i+1]])//i->i-1 與 i->i+1 線路不一致 即i爲換乘站
                tempTrans.push_back({path[i], line[path[i]*10000 + path[i+1]]});//記錄換乘站即換乘線路 !!!是i+1 不是i-1
        }
        tempTrans.push_back({dest, line[path[0]*10000 + path[1]]}); //存放終點
        if(trans.size() == 0 || trans.size() > tempTrans.size())    // !!!注意起始trans爲空的情況
            trans = tempTrans;
    }
    for(auto i : pre[stop])
        dfs(i);
    path.pop_back();
}
void bfs(){
    for(int i = 0; i < 10005; i++){
        pre[i].clear();
        dis[i] = INT_MAX;
    }
    queue<int> q;
    q.push(start);
    dis[start] = 0;
    while(!q.empty()){
        int p = q.front();
        q.pop();
        if(dis[p] > dis[dest])              // !!!剪枝
            continue;
        for(int i : graph[p]){              // !!!妙啊 i即與p相鄰的節點編號(而不是下標)
            if(dis[i] >= dis[p] + 1){
                if(dis[i] == INT_MAX)       // !!!妙啊 dis[i]爲最大值 就說明節點未被訪問過 從而加入隊列
                    q.push(i);
                if(dis[i] > dis[p] + 1){
                    dis[i] = dis[p] + 1;
                    pre[i].clear();         //最短路徑改變 清除以前記錄的前驅節點
                }
                pre[i].push_back(p);
            }
        }
    }
}
int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE
    scanf("%d", &N);
    int num, stop1, stop2;
    for(int i = 1; i <= N; i++){
        scanf("%d%d", &num, &stop1);                    // !!!
        for(int j = 1; j < num; j++, stop1 = stop2){    // !!!妙啊 還可以這樣j++, stop1 = stop2
            scanf("%d", &stop2);
            line[stop1*10000+stop2] = i;                // !!!妙啊 用整型stop1*10000+stop2表示stop1->stop2
            line[stop2*10000+stop1] = i;
            graph[stop1].push_back(stop2);              // 無向圖 鄰接表存儲
            graph[stop2].push_back(stop1);
        }
    }
    scanf("%d", &M);
    while(M--){             // !!!後自減 不是前自減
        scanf("%d%d", &start, &dest);
        bfs();
        path.clear();       //最短路徑記錄
        trans.clear();      //換乘站記錄
        dfs(dest);
        printf("%d\n", dis[dest]);  // !!!
        for(int i = 1; i < trans.size(); i++)
            printf("Take Line#%d from %04d to %04d.\n", trans[i].second, trans[i-1].first, trans[i].first);// !!!妙啊 別忘了first
    }
    return 0;
}

 

4,解題過程

跟着大佬敲的,沒什麼好說了。。。

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