题目描述
给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。
输入格式
第一行包含整数n和m。
接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。
输出格式
输出一个整数,表示1号点到n号点的最短距离。
如果路径不存在,则输出-1。
数据范围
1≤n≤500,
1≤m≤105,
图中涉及边长均不超过10000。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
本题可以看作是多源汇最短路的其中一例:单源汇最短路
其特点就是让我们求,从一个给定的源点【就是本题的1号点】,到其他各个顶点的最短距离
下面是给出的样例所形成的图
我们可以用一个邻接矩阵来存储这个图
我们用数组dist来表示从1号点到其他各个点的距离
如图可见我们的dist的初始化
本题要求我们求出从1号点到n号点的最短距离,也就是需要我们来求解dist[n]的最小值。
而我们的做法是分别一步一步的求出从1到各个点的最小值,也就是dist的所有的值(dist就
表示1号点到各个点的最小值)
我们是这么求的dist的值的:
【判断是否有一个点k使得点i经过点k后再到j的距离比i直接到j的距离短】
上面的是多源汇最短路的思路,而我们这里是多源汇的一种情况
我们只要考虑一个点k是否可以使得从1号点经过点k再到j比直接从1号点到j号点的距离段
其公式为
if(dist[j] > dist[k] + w[k][j]) dist[j] = dist[k] + w[k][j];
直到这里,我们就已经将本题分析完了,下面是我的代码,以及对代码的解释。
#include <cstring>
#include <iostream>
using namespace std;
const int N = 510, inf = 0x3f3f3f3f;
int n, m;
int w[N][N], dist[N];//w表示存储数据的图,dist表示Dijkstra算法里1号点到其他点的距离
bool st[N];//表示每个点的最短路是否确定了
long long sum;//可有可无
int main(){
cin >> n >> m;
//初始化w
memset(w, 0x3f, sizeof w);
//反正有自环限制,也不用全给g[i][i] = 0; 所以我们就用上面的代替了下面的
//wor (int i = 1; i <= n; i ++ )
// wor (int j = 1; j <= n; j ++ )
// iw (i == j) e[i][j] = 0;
// else e[i][j] = inw;
//读入边
for (int i = 1; i <= m; i ++ ){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
w[a][b] = min(w[a][b], c);//只保留最小的,解决掉重边
sum += c;//这个主要时用来判断dist[n]的,是否这样用看个人喜好了
}
//初始化dist数组,这里是1号点到其他各个点的距离
for (int i = 1; i <= n; i ++ )
dist[i] = w[1][i];//找到所有的原点为1号点到i的距离
dist[1] = 0;//1号点距离1号点为0,没毛病
st[1] = 1;
//Dijkstra算法的核心内容
for (int i = 1; i <= n; i ++ ){
//找到距离1号点最近的点
int minn = inf, u;
for (int j = 1; j <= n; j ++ )
if (!st[j] && dist[j] < minn){
minn = dist[j];
u = j;
}
//标记此点已经用过了
st[u] = true; //u这个点是距离1号点最近的点,这样dist[u]就确定下来了,给它标记一下
//判断如果i->k->j 的距离小于 i -> j的距离,那么我们就给他更新一下
for (int j = 1; j <= n; j ++ ){
if(w[u][j] < inf){//判断这个边是否存在
if (dist[j] > dist[u] + w[u][j])
dist[j] = dist[u] + w[u][j];
}
}
}
//wor (int i = 1; i <= n; i ++ ) cout << dist[i] << endl;
//iw (dist[n] == 0x3w3w3w3w) puts("-1");//两种形式都可以
if (dist[n] > sum) puts("-1");//无法从1走到n,输出-1
else cout << dist[n] << endl;
return 0;
}
下面给出一个简化后的代码,同样有解析
#include <cstring>
#include <iostream>
using namespace std;
const int N = 510;
int n, m;
int g[N][N];//g存入由数据生成的图
int dist[N];//dist表示从点1到其他点的当前最短距离
bool st[N];//表示这个点的最短路是否确定了
int Dijkstra(){
memset(dist, 0x3f, sizeof dist);//初始化距离,为正无穷大
dist[1] = 0;
for (int i = 1; i <= n; i ++ ){//迭代n次(也可以i从0开始到n - 1)
//确定最小的点
//每一次第一步先在还没有确定最短路的点当中的最小距离的点
int t = -1;//表示还没有确定
for (int j = 1; j <= n; j ++ )//遍历一下所有的点
if(!st[j] &&( t == -1 || dist[t] > dist[j])) t = j;
//st[j]表示当前点还没有确定最短路,
//t = -1 表示t还没有被确认
//dist[t] > dist[j] 表示t这个点的dist不是最短的
//开始把t加到我们的dist集合里面
st[t] = true;//我们会确定t这个点的距离问题
//拿t来更新其他点的距离
for (int j = 1; j <= n; j ++ ){
dist[j] = min(dist[j], dist[t] + g[t][j]);
//用1到t的距离(dist[t])和t到j的距离(g[t][j])来更新1到j的距离(dist[j])
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
else return dist[n];
}
int main(){
cin >> n >> m;
memset(g, 0x3f, sizeof g);//初始化
while (m -- ){
int a, b, c;
cin >> a >> b >> c;
g[a][b] = min(g[a][b], c);//只记录边的最短距离
}
int t = Dijkstra();
cout << t << endl;
return 0;
}