原题目: 1030 Travel Plan (30 分).
题意
给出城市数N(≤500,从0编号),道路数M,起点城市S和终点城市D(均不超过500的整数);接着M行,给出两城市及其间距离和费用。
👉 输出最短路径及其总距离和总费用(不止一条时,输出费用最少的那条)
分析
单源最短路径问题,且权值均为正数,还要维护最优路径——Dijkstra + DFS。👉 模板见:算法提高之图(《算法笔记》)
注意输出路径时倒序输出即可(存储最优路径时是逆序的)。
CODE
#include <iostream>
#include <vector>
using namespace std;
const int maxv = 510;
const int inf = 1000000000;
int G[maxv][maxv], di[maxv];
int cost[maxv][maxv]; //城间费用
vector<int> pre[maxv]; //前驱结点
bool vis[maxv]; //访问标记
vector<int> path, tmpPath; //最优路径,临时路径
int optc = inf; //最低总费用
int n, m, s, d;
void Dijkstra(int s);
void DFS(int v);
int main()
{
cin >> n >> m >> s >> d;
//建图
for ( int i=0; i<m; i++ ){
int a, b, dis, expend;
cin >> a >> b >> dis >> expend;
G[a][b] = G[b][a] = dis;
cost[a][b] = cost[b][a] = expend;
}
//Dijkstra得所有最短路径
Dijkstra(s);
//DFS找最优路径
DFS(d); //从终点往前找起点
//输出
for ( int i=path.size()-1; i>=0; i-- )
printf("%d ", path[i]);
cout << di[d] << " ";
cout << optc;
return 0;
}
void Dijkstra(int s){
//初始化
fill(di, di+maxv, inf);
di[s] = 0;
//遍历所有点
for ( int i=0; i<n; i++ ){
//找到u使d[u]最小
int u = -1, mindis = inf;
for ( int j=0; j<n; j++ ){
if ( vis[j]==false && di[j]<mindis ){
u = j;
mindis = di[j];
}
}
//若没找到
if ( u==-1 ) return;
//若找到则访问并作中介点
vis[u] = true;
//以u为中介点对其邻接点v优化
for ( int v=0; v<n; v++ ){
if ( vis[v]==false && G[u][v]>0 ){
if ( di[u]+G[u][v]<di[v] ){
di[v] = di[u] + G[u][v]; //优化最短距离数组
pre[v].clear(); //重新记录v的前驱结点
pre[v].push_back(u);
}
else if ( di[u]+G[u][v]==di[v] )
pre[v].push_back(u);
}
}
}
}
void DFS(int v){
//递归边界
if ( v==s ){
tmpPath.push_back(v); //加入最后一个结点
//计算费用
int tmpc = 0; //临时路径总费用
for ( int i=tmpPath.size()-1; i>0; i-- ){
int id = tmpPath[i], nextid = tmpPath[i-1];
tmpc += cost[id][nextid];
}
//看是否更优
if ( tmpc<optc ){
optc = tmpc; //更新最低费用
path = tmpPath; //更新最优路径
}
tmpPath.pop_back(); //删去刚加入的结点,准备返回上一层
return;
}
//递归式
tmpPath.push_back(v); //加入新结点
for ( int i=0; i<pre[v].size(); i++ ){ //深度优先遍历邻接点
DFS(pre[v][i]);
}
tmpPath.pop_back(); //遍历完所有前驱结点,将当前结点删除,准备返回上一层
return;
}