题目
A traveler’s map gives the distances between cities along the highways, together with the cost of each highway. Now you are supposed to write a program to help a traveler to decide the shortest path between his/her starting city and the destination. If such a shortest path is not unique, you are supposed to output the one with the minimum cost, which is guaranteed to be unique.
Input Specification:
Each input file contains one test case. Each case starts with a line containing 4 positive integers N N N, M M M, S S S, and D D D, where N ( ≤ 500 ) N (\le500) N(≤500) is the number of cities (and hence the cities are numbered from 0 0 0 to N − 1 N-1 N−1); M M M is the number of highways; S S S and D D D are the starting and the destination cities, respectively. Then M M M lines follow, each provides the information of a highway, in the format:
City1 City2 Distance Cost
where the numbers are all integers no more than 500, and are separated by a space.
Output Specification:
For each test case, print in one line the cities along the shortest path from the starting point to the destination, followed by the total distance and the total cost of the path. The numbers must be separated by a space and there must be no extra space at the end of output.
Sample Input:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
Sample Output:
0 2 3 3 40
题目大意
给出一张地图,地图包含各个城市之间的道路,每条路的距离以及花费,给出旅行者的出发地和目的地,要求计算出旅行者从出发地到目的地的最短路径,输出应包含出发地到目的地的各个城市,最短路径以及最短路径对应的花费
解题思路
整理思路应首先注意题目的核心问题,最短路径有多条,当有多条路径时,要选出花费最少的路径,基于这个问题,可以将问题分解为两个步骤:
- 求出最短路径(包含多条);可以采用dijkstra算法,用
vector
数组记录每个点在最短路径上的前驱节点,当然这样的前驱节点可能有多个(多条最短路径); - 根据求得的最短路径,从目的地到出发地反向dfs,当然这个dfs需要将所有的路径全部遍历一遍,选出花费最小的路径;
解题技巧
- 其实这个题用邻接矩阵存储便于实现,但考虑到邻接表适用更广泛,所以我采用邻接表;
- dijkstra在记录路径的时候,当更新路径时,要注意将
vector
数组清零再push()
; - dfs遍历时,因为要遍历所有的路径,所以每一层递归都要考虑回代的问题;
- 路径可以用临时变量
stack
保存,当遍历到出发点时,再与最终变量stack
比较,看是否更新
从的来说,这是一道比较经典的dijkstra加dfs经典题目,个人人为是一道很好的题目,需要多多练习这种类型的题目。具体的思路在代码中有详细的注释。
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
using namespace std;
const int INF = 0x7fffffff;
struct Node{
int id;
int weight;
int cost;
Node *next;
};
struct Root{
Node *next;
}root[500];
int n, m, s, d;
int visited[500];
int dist[500];
vector<int> path[500];
stack<int> anspath, temppath;
int anscost = INF, tempcost = 0;
void dijkstra(int s){
// 第一趟dijkstra,
visited[s] = 1;
Node *p = root[s].next;
while(p){
dist[p->id] = p->weight;
path[p->id].push_back(s);
p = p->next;
}
// 每趟dijkstra都会选取一个点,故只需要循环n-1次
for(int i=0; i<n-1; i++){
// 选取当前未被访问节点的最小dist值对应的点
int k, min = INF;
for(int j=0; j<n; j++){
if(!visited[j] && dist[j] < min){
k = j;
min = dist[j];
}
}
// 标记被访问
visited[k] = 1;
p = root[k].next;
while(p){
if(!visited[p->id]){
if(dist[k] + p->weight < dist[p->id]){ // 更新最短路径
dist[p->id] = dist[k] + p->weight;
path[p->id].clear();
path[p->id].push_back(k);
}
else if(dist[k] + p->weight == dist[p->id]){ // 保存另一条最短路径
path[p->id].push_back(k);
}
}
p = p->next;
}
}
}
void dfs(int d){
// 标记被访问,同时插入temppath中
visited[d] = 1;
temppath.push(d);
for(int i=0; i<path[d].size(); i++){ // 访问当前节点的多个前驱节点
int t = path[d][i];
if(visited[t]) // 避免往回走
continue;
int c;
Node *p = root[d].next;
while(p){ // 记录到达当前点的总的路径长度和花费
if(p->id == t){
c = p->cost;
tempcost += c;
break;
}
p = p->next;
}
dfs(t); // 遍历下一层
tempcost -= c; // 回代,因为此时再往回走
}
if(d == s){ // 更新最终结果
if(tempcost < anscost){
anscost = tempcost;
anspath = temppath;
}
}
visited[d] = 0;
temppath.pop();
}
int main(){
scanf("%d%d%d%d",&n, &m, &s, &d);
for(int i=0; i<n; i++){
root[i].next = NULL;
}
// 建立矩阵存储
for(int i=0; i<m; i++){
int x, y, w, c;
scanf("%d%d%d%d", &x, &y, &w, &c);
Node *p = new Node;
Node *q = new Node;
p->id = y;
q->id = x;
p->weight = q->weight = w;
p->cost = q->cost = c;
p->next = root[x].next;
q->next = root[y].next;
root[x].next = p;
root[y].next = q;
}
// 初始化dist和visited数组
fill(dist, dist+n, INF);
fill(visited, visited+n, 0);
// dijkstra求出所有最短路径
dijkstra(s);
// 再次初始化visited数组,以进行dfs
fill(visited, visited+n, 0);
// dfs求出最终路径,并且计算了最少花费
dfs(d);
// 输出路径
while(!anspath.empty()){
printf("%d ", anspath.top());
anspath.pop();
}
printf("%d %d\n", dist[d], anscost);
return 0;
}