题目大意:
给一个无向图,求出从一点到另外一点的最短路径,这里的最短,指的是从起点到终点所经历的点的数目最少,如果有多条,输出中转站点最少的。
算法思路:
一开始想到的是用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
)
提交结果:
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;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。