1030. Travel Plan (30)

来源:互联网 发布:vb.net加载dll 编辑:程序博客网 时间:2024/05/01 12:35

原题如下:

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, M, S, and D, where N (<=500) is the number of cities (and hence the cities are numbered from 0 to N-1); M is the number of highways; S and D are the starting and the destination cities, respectively. Then 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 30 1 1 201 3 2 300 3 4 100 2 2 202 3 1 20
Sample Output
0 2 3 3 40



题目要求找一个图中找出源点和结点间的所有最短距离,并且针对第二个权值(花费)筛选出一条最佳路径。

要实现单源最短路径算法,使用Dijkstra即可,但是还要针对多个结果按照路径筛选,只需要对Dijkstra进行小小的变种即可。

这道题我完整参考了sunbaigui的解法,下面我将简单介绍这个解法

①为了记录路径,把每个城市定义成一个结点:

typedef struct Node{int dis, cost, path;Node(){dis=INF; cost=INF; path=-1;}}Node;
我们知道Dijkstra中需要一个数组minDist记录从源点到各个点的最短距离,此题我们还需要一个minCost记录从源点到当前点的最少花费,以便判断是否要在路径等长时更新路径。使用Node结点,用cost和dis分别代表源点到当前点的花费和距离,而path用于记录前驱结点,-1代表无前驱,即源点,这样就方便的存储了多个量。

将Node放入容器vector,即可记录全图所有结点。

②图用edge存储:

typedef struct Edge{int end, dis, cost;Edge(int e, int d, int c){end = e; dis = d; cost = c;}};
将Edge放入容器vector,每条边代表从当前edge的索引到end之间的dis、cost。

③Dijkstra的另一种写法:

首先,传统的Dijkstra我们会提前更新源点的邻接点的minDist为源点到这个点的距离,方便后面的查找,而这个算法给出的是将这个操作融入到Dijkstra的算法内部,实现原理为首先初始化到源点的距离为0,但并不加入集合,这样在第一轮扫描中会发现源点,然后针对源点更新所有邻接点,这样写就避免了多余的初始化操作,实现了操作的统一,非常巧妙。

其次,在发现路径相等时,需要比较经过中间点到邻接点的花费是否比原来少,如果少也要更新,路径更新的方法为把前驱结点设置为中间点。

④路径的回溯:

经过Dijkstra算法,我们可以通过终点的path一路向前回溯,直到找到-1,也就是源点,把回溯过程中的结点压入一个堆栈,然后再用堆栈输出,即可正序打印路径。

代码如下:

#include <algorithm>#include <iostream>#include <vector>#include <stack>#include <stdio.h>using namespace std;#define  INF 0x6FFFFFFF// 城市结点,每个结点记录着到达这个城市的最小花费和最短路径,通过一连串的城市结点可以回溯到源点。typedef struct Node{int dis, cost, path;Node(){dis=INF; cost=INF; path=-1;}}Node;// 每条边记录着从边数组索引到边的end成员之间的路径信息typedef struct Edge{int end, dis, cost;Edge(int e, int d, int c){end = e; dis = d; cost = c;}};int n, m, s, t;vector<vector<Edge> > edge; // 通过边结构体保存整个图vector<int> path;vector<Node> city; // 所有城市结点vector<bool> visited; // 用于Dijkstra的访问标记,有的书上将visited理解为Set,即把结点加入集合,二者的实现目的都是防止结点被重复访问。int FindMin() // Dijkstra的最短路径搜索,city在这里的作用相当于minDist数组,i索引代表从源点到i的最短距离。{int mmin = INF;int k = -1;for (int i = 0; i < n; ++i){if(!visited[i] && city[i].dis < mmin){mmin = city[i].dis;k = i;}}return k;}void Dijkstra(int s, int t){visited.assign(n, false);city.clear();city.resize(n); // 根据构造方法,最初到每个城市结点的距离都是INF,每个结点都没有前驱结点(-1表示)。city[s].dis = 0; city[s].cost = 0; // 初始化到源点的距离为0,花费为0.// 常规的Dijkstra是先把所有到源点的距离初始化,但是这里没有。// 他的原理是先不把源点加入集合,而是在第一轮循环中拿到源点(因为只有到源点不是INF)// 然后实现所有到源点的初始化,实现了操作的统一性,非常巧妙。while(true){int u = FindMin();if(u == -1)return;visited[u] = true; // 找到从源点到各点中路径最短且未访问的点u,设为访问,并且针对u更新所有邻接点。for (int i = 0; i < edge[u].size(); ++i){int v = edge[u][i].end; // v是u的一个邻接点int cost = edge[u][i].cost; // cost是u到这个邻接点v的距离int dis = edge[u][i].dis; // dis是到u到这个邻接点v的路程if (!visited[v]){if (city[v].dis > city[u].dis+dis) // 如果通过中间点u到达v的距离缩短,应该更新路径为通过u到达v。{city[v].dis= city[u].dis+dis;city[v].cost = city[u].cost+cost;city[v].path = u;}else if (city[v].dis == city[u].dis+dis // 如果距离不变,但是花费变少,仍然要更新&& city[v].cost > city[u].cost+cost){city[v].cost = city[u].cost+cost;city[v].path = u;}}}}}void OutputPath(int t){    // 从终点回溯到源点,到达源点的标志是path=-1    // 用一个栈实现反向遍历正向输出stack<int> ans;ans.push(t);while(city[t].path != -1){t = city[t].path;ans.push(t);}while (!ans.empty()){printf("%d ", ans.top());ans.pop();}}int main(){while (scanf("%d%d%d%d",&n,&m,&s,&t)!=EOF){edge.clear();edge.resize(n);while (m--){int u, v, d, c;scanf("%d%d%d%d",&u,&v,&d,&c);edge[u].push_back(Edge(v, d, c));edge[v].push_back(Edge(u, d, c));}//dijkstraDijkstra(s, t);OutputPath(t);printf("%d %d\n",city[t].dis, city[t].cost);}return 0;}



0 0
原创粉丝点击