作者 龚雄兴
单位 湖北文理学院
本题目要求通过读入无向网的边的信息(省略了各顶点的信息,仅用顶点编号来表示),构造图,并利用Dijkstra算法,求出指定源点到其它各点的最短路径。
样例">输入样例:
第一行,两个整数,顶点数vN和边数eN。
以后若干行,是相关边的信息,无向图的边是对称的,只输入一半的边(小编号到大编号的,间以空格),最后两行各一个整数,前一个指定源点,后一个指定的查询的终到点。
(注意,示例中34条边,只输入了17条边的信息)
10 34
0 1 2
0 3 5
1 2 5
1 3 2
2 4 8
2 5 4
3 5 4
3 6 2
4 7 5
4 5 2
5 6 3
5 7 9
5 8 7
6 8 7
7 8 3
7 9 4
8 9 8
0
8
输出样例:
在一行中输出从源点到指定终点的短路径及代价,注意:所有符号均为西文符号。
0-->1-->3-->6-->8:13
个人思路:
朴素dijkstra+打印路径
dijkstra算法思路:
dijkstra求的是单源最短路问题
int g[i][j] : 从点i 到 点j 的距离
int dist[N] : 从起点到每一个点的最短距离
bool st[N] : 当前是否确定了最短路
1.初始化距离
将起点初始化为0 ,其他所有点都等于正无穷(一个比较大的数)
2.for循环N次
每次找到未确定最短路且距离最近的点 t 。
接下来 st [ t ] = true;
然后用 点t 更新其他点的距离。
看一下 从点t 出发到达的每个点到起点的距离,能不能用起点到点t再到这个点的距离更新。
即假设 点t 能到达 点x ,判断dist[x] 是否小于dist[t] + g[t][x],小于即更新dist[x]。
最后我们得到的dist数组即为每个点到起点的最短距离
3.打印路径
考虑dijkstra算法特点,每次我们选出一个当前的最短距离点t ,用它去更新其余点,如果有点被更新,那么这个点当前的最短路径中,点t 一定是这个点的前一个点,根据这个特点,我们可以用一个path数组,保存这个点的前一个节点,当所有点的最短路径都确定以后,我们的path数组保存的刚好是每个点的最短路的前一个节点,因为dijkstra是单源最短路,path保存每个点最短路径的前一个点,而前一个点的最短路径必定包含在后一个的最短路径里面。因此他们可以连起来。此时,我们利用一个循环,便可将最短路径倒序输出。
#include <bits/stdc++.h>
using namespace std;
const int N = 100;
const int INF = 100000;
int n, m;
int g[N][N]; //利用邻接矩阵保存两个点的距离
int dist[N]; //保存每个点到起点的最短距离
bool st[N]; //每个点是否已经确定最短距离
int path[N]; //每个点最短路径的前一个节点
int dijkstra(int sec,int n)
{
memset(dist, 0x3f, sizeof dist); //初始化所有距离为最大
dist[sec] = 0; //初始化起点距离为0
for (int i = 0; i < n - 1; i++) //循环找到起点最短的且未确定最短距离的点t
{
int t = -1;
for (int j = 0; j <= n; j++)
if (!st[j] && (t == -1 || dist[t] > dist[j])) {
t = j;
}
for (int j = 0; j <= n; j++) {
if (dist[j] > dist[t] + g[t][j]) {
path[j] = t; //若从起点到点j的距离被从起点到点t,再从点t到点j更新,
//那么t就是j的前驱节点,保存到path[j]里
}
dist[j] = min(dist[j] , dist[t] + g[t][j]);//更新最短路
}
st[t] = true; //点t的最短路径已经确定
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main() {
memset(g, 0x3f, sizeof g); //初始化邻接矩阵距离无穷大
memset(path, -1, sizeof path); //初始化每个点的前驱为-1,表示没有前驱
cin >> n >> m;
m /= 2;
while (m--) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
//由于是无向图,需要加入两个边,a到b,和b到a
g[a][b] = min(g[a][b], c);
g[b][a] = min(g[b][a], c);
}
int s, e; // s为起点,e为终点
scanf("%d", &s);
scanf("%d", &e);
if(s==e){
cout<<s<<"-->"<<e<<":0";
return 0;
}
int ret = dijkstra(s, n-1); //传入起点,和点的个数,由于点从0开始,所以应该传入n-1
stack<int> stk; //根据path特点,我们用一个栈保存,最后弹栈输出时,即为正序
int k = e;
while (true) {
if (k == s) break;
stk.push(path[k]);
k = path[k];
}
while (!stk.empty()) {
cout << stk.top() << "-->";
stk.pop();
}
cout << e;
printf(":%d", dist[e]);
}