**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,解题过程

跟着大佬敲的,没什么好说了。。。

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