定义概览
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。注意该算法要求图中不存在负权边。
我的理解
从开始顶点出发,先将开始顶点吃入(即标记已访问)
1。找其最短的边所连的顶点
2。将其吃入(即标记已访问)
3。遍历与其相连的其他顶点
4。若从V 到W距离小于之前到W的距离,则修改(更新)
5。可以用个堆栈来存储路径
6。循环1~4 更新距离, 直至所有的顶点都已经吃入
(1)dist 表示从这个点(数组序号)到原点的最短距离,每次也更新这里
(2)path 用来表示路径
(3)注意:第一次是要把入口顶点所在的边吃入先——–这步初始化别忘了
/* 邻接矩阵存储 - 有权图的单源最短路算法 */
Vertex FindMinDist( MGraph Graph, int dist[], int collected[] )
{ /* 返回未被收录顶点中dist最小者 */
Vertex MinV, V;
int MinDist = INFINITY;
for (V=0; V<Graph->Nv; V++) {
if ( collected[V]==false && dist[V]<MinDist) {
/* 若V未被收录,且dist[V]更小 */
MinDist = dist[V]; /* 更新最小距离 */
MinV = V; /* 更新对应顶点 */
}
}
if (MinDist < INFINITY) /* 若找到最小dist */
return MinV; /* 返回对应的顶点下标 */
else return ERROR; /* 若这样的顶点不存在,返回错误标记 */
}
bool Dijkstra( MGraph Graph, int dist[], int path[], Vertex S )
{
int collected[MaxVertexNum];
Vertex V, W;
/* 初始化:此处默认邻接矩阵中不存在的边用INFINITY表示 */
for ( V=0; V<Graph->Nv; V++ ) {
dist[V] = Graph->G[S][V];
if ( dist[V]<INFINITY )
path[V] = S;
else
path[V] = -1;
collected[V] = false;
}
/* 先将起点收入集合 */
dist[S] = 0;
collected[S] = true;
while (1) {
/* V = 未被收录顶点中dist最小者 */
V = FindMinDist( Graph, dist, collected );
if ( V==ERROR ) /* 若这样的V不存在 */
break; /* 算法结束 */
collected[V] = true; /* 收录V */
for( W=0; W<Graph->Nv; W++ ) /* 对图中的每个顶点W */
/* 若W是V的邻接点并且未被收录 */
if ( collected[W]==false && Graph->G[V][W]<INFINITY ) {
if ( Graph->G[V][W]<0 ) /* 若有负边 */
return false; /* 不能正确解决,返回错误标记 */
/* 若收录V使得dist[W]变小 */
if ( dist[V]+Graph->G[V][W] < dist[W] ) {
dist[W] = dist[V]+Graph->G[V][W]; /* 更新dist[W] */
path[W] = V; /* 更新S到W的路径 */
}
}
} /* while结束*/
return true; /* 算法执行完毕,返回正确标记 */
}
题目链接:https://pta.patest.cn/pta/test/1342/exam/4/question/23160
坑点:
高速公路是双方向的。一开始我以为是单方向的,然后就只过了一个用例。第一次练这题,所以有点啰嗦,请见谅!
//07-图6 旅游规划 (25分)
#include<cstdio>
using namespace std;
#define MAXN 505
#define INFINITY 505
struct City{
int len;
int fees;
};
int N; //城市的个数 编号 0 ~ (N - 1)
int M; //高速公路的条数
int S; //出发地的城市编号
int D; //目的城市编号
City city[MAXN][MAXN]; //图
int flag[MAXN]; //标记
City dist[MAXN]; //dist表示 这个点到原点的最短路径
int Length; //路径长度
int Fees; //收费额
void init(){
for(int i = 0; i < N; ++i){
for(int j = 0; j < N; ++j){
city[i][j].len = INFINITY;
city[i][j].fees = INFINITY;
}
}
for(int i = 0; i < N; ++i){
dist[i].len = INFINITY;
dist[i].fees = INFINITY;
}
}
void setDistValue(int s, int i, int j){
dist[S].len = i;
dist[S].fees = j;
}
int findMinDist(){
City minDist;
minDist.fees = INFINITY;
minDist.len = INFINITY;
int V; //用于返回的顶点
for(int i = 0; i < N; ++i){
if(flag[i] == 0){
if(dist[i].len < minDist.len){
minDist.len = dist[i].len;
minDist.fees = dist[i].fees;
V = i;
}else if(dist[i].len == minDist.len){
if(dist[i].fees < minDist.fees)
minDist.fees = dist[i].fees;
}
}
}
if(minDist.len < INFINITY)
return V; //返回对应的顶点下标
else return -1; //这样的顶点不存在,返回错误标记
}
void dijkstra(){
setDistValue(S, 0, 0); //将起点吃入集合
flag[S] = 1; //标记
for(int i = 0 ;i < N; ++i){
dist[i].len = city[S][i].len;
dist[i].fees = city[S][i].fees;
}
int V; //用来表示顶点下标
while(1){
V = findMinDist();
if(V == -1) //这样结点不存在
break;
flag[V] = 1; //吃入
for(int i = 0; i < N; ++i){ //对图中的每个顶点
if(flag[i] == 0 && city[V][i].len < INFINITY){ // W是V的邻边且未被吃入
if(city[V][i].len < 0) //为负边
return ; //不能正确处理,返回错误标记
if(dist[V].len + city[V][i].len < dist[i].len){ //吃入V使得dist[i]变小
dist[i].len = dist[V].len + city[V][i].len;
dist[i].fees = dist[V].fees + city[V][i].fees;
}else if(dist[V].len + city[V][i].len == dist[i].len){ //吃入V等于dist[i]
if(dist[V].fees + city[V][i].fees < dist[i].fees) //路费比其少则更新
dist[i].fees = dist[V].fees + city[V][i].fees;
}
}
}
}
}
int main(void){
scanf("%d%d%d%d", &N, &M, &S, &D);
init(); //初始化
int beginCity;
int endCity;
int len;
int fees;
for(int i = 0; i < M; ++i){
scanf("%d%d%d%d", &beginCity, &endCity, &len, &fees);
city[beginCity][endCity].len = len;
city[beginCity][endCity].fees = fees;
city[endCity][beginCity].len = len;
city[endCity][beginCity].fees = fees;
}
dijkstra();
// for(int i = 0 ; i < N; ++i)
// printf("i=%d len=%d fees=%d\n", i, dist[i].len, dist[i].fees);
printf("%d %d", dist[D].len, dist[D].fees);
return 0;
}