- <p><span style="font-size: 18px;">最短路主要有四部分。</span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span>一</span><span style="font-family: Arial, Helvetica, sans-serif;">迪杰斯特拉思想</span></p>
- //Dijkstra算法用于计算一个节点到其它所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点。
- //基本思想是:设置顶点集合S不断地做贪心选择来扩充这个集合
- //初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。
- #include <stdio.h>
- #define maxnum 100
- #define maxint 999999
- int dist[maxnum]; //表示当前点到源点的最短路径长度
- int prev[maxnum]; //记录当前点的前一个结点
- int c[maxnum][maxnum]; //记录图的两点间路径长度
- int n,line; //图的结点数和路径数
- void Dij(int n,int v,int *dist, int *prev, int c[maxnum][maxnum]) //v代表源点吗?
- {
- int s[maxnum] = {0};//判断是否已将该点存入到s数组中
- int i;
- for(i = 1;i<=n;i++)
- {
- dist[i] = c[v][i];//将从源点到i点的初始距离存入数组dist中
- s[i] = 0;//初始化为全部没有用过该点
- if(dist[i] == maxint)//如果源点到该点的距离为无穷大,则该点前面没有连接点
- prev[i] = 0;
- else
- prev[i] = v;
- }
- dist[v] = 0; //源点到源点的距离初始化为0
- s[v] = 1;//把源点存入数组s
- //依次将未放入集合s中的节点,取dist【】最小值的节点放进去
- //一旦s包含了所有v中顶点,dist就记录了从源点到所有其他顶点之间的最短路径
- //注意是从第二个节点开始,第一个结点表示源点
- for(i = 2;i<=n;i++)
- {
- int tmp = maxint;
- int u = v;
- for(int j = 1;j<=n;j++) //找出当前未使用的点j的dist【j】最小值
- {
- if((!s[j]) && dist[j] < tmp)
- {
- u = j; //u储存当前邻接点中距离最小的点的号码
- tmp = dist[j];
- }
- }
- s[u] = 1; //一次for循环结束后,便找出了所有未使用的点中距离最小的点,将此点存入集合s中
- //更新dist值
- for(int j = 1;j<=n;j++)
- {
- if((!s[j]) && c[u][j]<maxint) //确定该点没有加入s集合,并且存在这段距离,即经过该点
- {
- int newdist = dist[u] + c[u][j];//判断经过该点到j的距离和不经过该点到j的距离的大小
- if(newdist < dist[j])
- {
- dist[j] = newdist;
- prev[j] = u;
- }
- }
- }
- }
- }
- //查找从源点v到终点u的路径,并输出
- void searc(int *prev,int v,int u)
- {
- int que[maxnum]; //该数组保存路径
- int tot = 1;
- que[tot] = u; //从终点开始找,一直找到源点v并记录路径
- tot++;
- int tmp = prev[u];
- while(tmp != v)
- {
- que[tot] = tmp;
- tot++;
- tmp = prev[tmp];
- }
- que[tot] = v;
- for(int i = tot;i>=1;i--)//输出
- {
- if(i!=1)
- printf("%d->",que[i]);
- else
- printf("%d\n",que[i]);
- }
- //注意,若题目要求输出距离,则直接输出dist【u】即可
- }
- int main()
- {
- scanf("%d%d",&n,&line);
- int p,q,len;
- for(int i = 1;i<=n;i++)
- {
- for(int j = 1;j<=n;j++)
- c[i][j] = maxint;
- }
- for(int i = 1;i<=line;i++)
- {
- dist[i] = maxint;
- }
- for(int i = 1;i<=line;i++)
- {
- printf("%d%d%d",&p,&q,&len);
- if(len<c[p][q])
- {
- c[p][q] = len;
- c[q][p] = len;
- }
- }
- for(int i = 1;i<=n;i++)
- {
- for(int j = 1;j<=n;j++)
- {
- printf("%8d",c[i][j]);
- }
- printf("\n");
- }
- Dij(n,1,dist,prev,c);
- printf("从源点到终点的最短路径长度为%d\n",dist[n]);
- searc(prev,1,n);
- return 0;
- }
- //下面运用弗洛伊德(Floyd)来解决有关问题
- //在该思想中,数组a【】【】初始化为个顶点之间的距离,最后储存各定点之间的最短距离。
- //path【】【】数组保存最短路径,与当前迭代的次数有关,初始化都为-1,即没有中间顶点。
- //在求a【i】【j】的过程中,path[i][j]存放从顶点i到顶点j的中间顶点的编号不大于k的最短路径上前一个结点的编号
- //数组a[][]其实是有向图的邻接矩阵,此类矩阵可以表示无向
- //这个程序是
- #include <stdio.h>
- #include <string.h>
- struct MGraph
- {
- char vertex[1000]; // 储存各个顶点
- int edges[1000][1000]; //邻接矩阵
- int n,e; // n代表顶点个数,e代表边数
- };
- void ppath(int path[][1000],int i,int j) 调用函数,输出路径
- {
- int k;
- k = path[i][j];
- if(k == -1)
- return;
- ppath(path,i,k);
- printf("%d,",k);
- ppath(path,k,j);
- }
- void CreateMGraph(MGraph &G)
- {
- int i,j,k,p;
- scanf("%d%d",&G.n,&G.e); //输入顶点数和边数
- for(i = 0;i<G.n;i++) //输入各个顶点
- {
- scanf("%d",&G.vertex[i]);
- }
- for(i = 0;i<G.n;i++) // 初始化邻接矩阵。若是对角线,即是自己到自己的距离,初始化为0
- {
- for(j = 0;j<G.n;j++)
- {
- G.edges[i][j] = 100000;
- if(i == j)
- G.edges[i][j] = 0;
- }
- }
- for(i = 0;i<G.e;i++) // 输入边的前驱后驱以及权值
- {
- scanf("%d%d%d",&j,&k,&p);
- G.edges[j][k] = p;
- }
- }
- void Dispath(int A[][1000],int path[][1000],int n)
- {
- int i,j;
- for(i = 0;i<n;i++)
- {
- for(j = 0;j<n;j++)
- {
- if(A[i][j] == 100000)
- {
- if(i!=j)
- printf("从%d到%d没有路径\n",i,j);
- }
- else
- {
- printf("从%d到%d=>路径长度:%d路径:",i,j,A[i][j]);
- printf("%d",i);
- ppath(path,i,j);
- printf("%d\n",j);
- }
- }
- }
- }
- void Floyd(MGraph &G)
- {
- int A[1000][1000],path[1000][1000];
- int i,j,k;
- //初始化a【】【】为最开始的距离,path【】【】代表最短路径,与当前迭代的次数有关
- for(i = 0;i<G.n;i++)
- {
- for(j = 0;j<G.n;j++)
- {
- A[i][j] = G.edges[i][j];
- path[i][j] = -1;
- }
- }
- for(k = 0;k<G.n;k++)// 运用弗洛伊德进行最短路搜索,k作为中间点
- {
- for(i = 0;i<G.n;i++)
- {
- for(j = 0;j<G.n;j++)
- {
- if(A[i][j] > A[i][k] + A[k][j])
- {
- A[i][j] = A[i][k] + A[k][j];
- path[i][j] = k;
- }
- }
- }
- }
- Dispath(A,path,G.n);
- }
- int main()
- {
- MGraph G;
- CreateMGraph(G);
- Floyd(G);
- return 0;
- }
- </pre><pre code_snippet_id="204097" snippet_file_name="blog_20140225_5_5274998" name="code" class="html">
- </pre><pre code_snippet_id="204097" snippet_file_name="blog_20140225_5_5274998" name="code" class="html">
贝尔曼—福特思想
- //迪杰斯特拉算法是处理单源最短路径的算法,但它局限于边的权值非负的情况
- //贝尔曼——福特就可以解决此类问题
- //思想:给定图G(V, E)(其中V、E分别为图G的顶点集与边集),源点s,
- //数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n]为, Distant[s]为0;
- //以下操作循环执行至多n-1次,n为顶点数:
- //对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
- //若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
- //为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的边,则图中存在负环路,
- //即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。
- #include <stdio.h>
- #include <stdlib.h>
- #define maxnum 2000
- #define maxint 999999
- typedef struct
- {
- int v,u; //终点和起点
- int weight; //两点之间的权值
- } Edge;
- Edge edge[maxnum]; //保存边的值
- int dist[maxnum]; //保存结点到原点的最短距离
- int nodenum,edgenum,source; //结点数,边数,源点
- void init()
- {
- scanf("%d%d%d",&nodenum,&edgenum,&source); //输入结点数,边数,源点
- int i;
- for(i = 1;i <= nodenum;i++)
- dist[i] = maxint; //把结点到原点的距离都初始化为最大
- dist[source] = 0; //节点到节点的距离初始化为0
- for(i = 1;i<=edgenum;i++)
- {
- scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].weight); //点u到点v的距离
- if(edge[i].u == source)
- dist[edge[i].v] = edge[i].weight; //如果起点是源点,则把点v到点u(源点)的距离更改为权值weight
- }
- }
- int BF() //该函数用于计算最小距离和判断是否有负权值
- {
- int i,j,flag = 1;
- for(i = 1;i<=nodenum-1;i++) //外层循环的次数是结点数减一
- {
- for(j = 1;j <= edgenum;j++)//内层循环是边的数目,也就是每次都遍历整个边表
- {
- if(dist[edge[j].v] > dist[edge[j].u] + edge[j].weight)
- dist[edge[j].v] = dist[edge[j].u] + edge[j].weight;
- //如果从源点到第v个点的距离大于从源点到第u个点的距离加上从u点到v点的距离,就改变从源点到第v个点的距离
- }
- }
- for(i = 1;i<=edgenum;i++) //再一次进行最短路搜寻,在上面的循环中已经求出了最短路线,如果还能求出最短路线,则会出现负环路,每进去一次,都会减小
- {
- if(dist[edge[i].v > dist[edge[i].u + edge[i].weight)
- {
- flag=0;
- break;
- }
- }
- return flag;
- }
- int main()
- {
- init();
- if(BF())
- {
- for(int i = 1;i<=nodenum;i++)
- printf("%d\n",dist[i]);
- }
- else
- printf("-1\n");
- return 0;
- }