2

题目大意:

给一个无向图,求出从一点到另外一点的最短路径,这里的最短,指的是从起点到终点所经历的点的数目最少,如果有多条,输出中转站点最少的。

算法思路:

一开始想到的是用Dijkstra算法求解该问题,但是Dijkstra算法更适合求解第一标尺为边权相关问题,所以想到了DFS,并设置其参数depth,用来记录在从起点到终点的遍历过程中所经历的结点个数,然后采用全局量minDepth保存最小的depth,为了方便判断是否是中转结点问题,使用邻接矩阵存储两个连接的点的边对应的线路,比如2000与2001连通,该边是Line# 4的一部分,则G[2000][2001]=4;使用全局变量$minTranNum$记录乘坐的最少线路数目。

DFS遍历方法:

DFS函数中设置参数为当前结点st,终点e和深度depth,使用vector<int> result保存最优的路径,vector<int> path暂存当前路径, 首先访问当前节点(保存到path中,并设置访问标记为true),然后判断是否到达终点,如果是,首先计算当前路径的中转节点数目,这里的计算方法是使用一个set集合$diffLines$添加所有的 线路,其大小就是乘坐的线路数目(其大小减一就是中转节点数目),如果minDepth>depth说明当前路径更短,那么就更新最优路径和最优距离和乘坐线路数目,如果 长度相同但是乘坐的线路最少,那么更新最优路径和乘坐线路数目,最后回溯。如果没有到达终点,就递归遍历每一个没有访问的领接点,如果所有的领接点都已经访问完毕, 那么就回溯。

注意点:

  • 1、每一次查询都得将全局变量minDepth,minTranNum,visited初始化。
  • 2、如果程序没有报错,没有输出结果,查看是否DFS函数有回退操作(path.pop_back(),visited[st]=false)

提交结果:

截屏2020-11-26 下午2.10.28.png

AC代码:

#include<cstdio>
#include<vector>
#include<unordered_set>
#include<cstring>

using namespace std;

int G[10000][10000];//G[i][j]=4表示i和j相连接的边在第4条地铁上
vector<int> path;// 暂存当前搜索到的路径
vector<int> result;// 最优路径
vector<int> Adj[10000];// 邻接表
bool visited[10000];

int minDepth;// 最短长度
int minTranNum;//乘坐的最少线路数目

void DFS(int st, int e, int depth){
    // 首先访问当前节点
    path.push_back(st);
    visited[st] = true;
    if(st == e){
        // 到达终点
        // 计算乘坐的线路数目
        unordered_set<int> diffLines;// 不同的线路数目
        for (int i = 0; i < path.size() - 1; ++i) {
            diffLines.insert(G[path[i]][path[i+1]]);
        }
        if(minDepth>depth){
            // 当前路径更短
            result = path;
            minDepth = depth;
            minTranNum = diffLines.size();
        } else if(minDepth==depth&&minTranNum>diffLines.size()){
            // 长度相同,但是中转次数最少
            result = path;
            minTranNum = diffLines.size();
        }
        // 回溯
        path.pop_back();
        visited[st] = false;
        return;
    }
    // 访问每一个领接点
    for (int next : Adj[st]) {
        // 领接点
        if(!visited[next]){
            DFS(next, e, depth + 1);
        }
    }
    path.pop_back();// 回溯
    visited[st] = false;
}

void print(int start,int end){
    printf("%d\n",minDepth);
    int current_line = G[result[0]][result[1]];// 当前线路
    int s = start;// 乘坐当前线路的起点
    for (int i = 1; i < result.size()-1; ++i) {
        if(G[result[i]][result[i+1]]!=current_line){
            // i为换乘站
            printf("Take Line#%d from %04d to %04d.\n",current_line,s,result[i]);
            s = result[i];
            current_line = G[result[i]][result[i+1]];
        }
    }
    // 一直没有换乘或者当前位最后一次中转地铁线
    printf("Take Line#%d from %04d to %04d.\n",current_line,s,end);
}

int main(){
    int N;
    scanf("%d",&N);
    for (int i = 0; i < N; ++i) {
        int M;
        scanf("%d",&M);
        int a,b;
        for (int j = 0; j < M; ++j) {
            if(j==0){
                scanf("%d",&a);
            } else {
                scanf("%d",&b);
                G[a][b] = G[b][a] = i+1;
                Adj[a].push_back(b);
                Adj[b].push_back(a);
                a = b;
            }
        }
    }
    int K;
    scanf("%d",&K);
    int start,end;
    for (int k = 0; k < K; ++k) {
        scanf("%d %d", &start, &end);
        // 每次查询都得初始化
        minDepth = 0x3fffffff;
        minTranNum = 0x3fffffff;
        memset(visited,0, sizeof(visited));
        path.clear();
        result.clear();
        DFS(start, end, 0);
        print(start, end);// 输出出行方案
    }
    return 0;
}

乔梓鑫
569 声望17 粉丝

主要分享个人学习经验和心得