最短路径
来源:互联网 发布:淘宝助理5.6.4.1 编辑:程序博客网 时间:2024/04/29 06:55
一.定义
•从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径——最短路径。
•最短路径一共有四种算法来实现,今天在这里我先介绍下其中的三种方法,Floyd算法,Dijkstra算法,Bellman-Ford算法。其中Floyd算法可以求解任意两点间的最短路径的长度,它的时间复杂度是O(n³)
• 而Dijkstra算法 Bellman-Ford算法是以某一点为起点来求解其到某一点的最短路径的长度。
二.Floyd算法
1.基本思想:
从任意节点A到任意节点B的最短路径不外乎2种可能,1是直接从A到B,2是从A经过若干个节点到B。所以,我们假设dis(AB)为节点A到节点B的最短路径的距离,对于每一个节点K,我们检查dis(AK)
+ dis(KB) < dis(AB)是否成立,如果成立,证明从A到K再到B的路径比A直接到B的路径短,我们便设置 dis(AB) = dis(AK) +
dis(KB),这样一来,当我们遍历完所有节点K,dis(AB)中记录的便是A到B的最短路径的距离。这道题就运用到了dp的思想。
或许有些人听了上面的话后会这样写代码
for (int i=1; i<=n;i++) { for(int j=1;j<=n;j++) { for (int k=1; k<=n;k++) { dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); } }}
但是这里我们要注意循环的嵌套顺序,如果把检查所有节点K放在最内层,那么结果将是不正确的,为什么呢?因为这样便过早的把i到j的最短路径确定下来了,而当后面存在更短的路径时,已经不再会更新了。
就像下面出现的情况一样
按照上面你的代码从A到B只能找到一条路径 那就是AB 权值为26 但显然这是不对的 对于AC-CD-DB这条路而已 加起来权值也仅仅只有19 权值小于直接A到B 造成错误的原因就是我们把检查所有节点K放在最内层,造成过早的把A到B的最短路径确定下来了,当确定A->B的最短路径时dis(AC)尚未被计算。所以,我们需要改写循环顺序,如下:
for(int k=1;k<=n;k++)//中间点 { for(int i=1;i<=n;i++)//起始点 { for(int j=1;j<=n;j++)//终点 { if(i!=j&&j!=k) { dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); } } } }
2.Floyd算法模板
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <map>#include <cmath>#include <queue>#include <string>#include <vector>#include <set>using namespace std;#define ll long long#define sc(x) scanf("%d",&x)#define dsc(x,y) scanf("%d%d",&x,&y)#define sssc(x) scanf("%s",s)#define sdsc(x,y) scanf("%s %s",x,y)#define ssc(x,y,z) scanf("%d%d%d",&x,&y,&z)#define pr(x) printf("%d\n",x)#define FOR(i,n,o) for(int i=o;i<=n;i++)#define lcr(a,b) memset(a,b,sizeof(a))#define Inf 1<<29int n,m;int mp[100][100];int main(){ while(~dsc(n,m))//n个点 m条路 { FOR(i,n,1) { FOR(j,n,1) { if(i!=j) { mp[i][j]=Inf;//初始化为极大值 } else { mp[i][j]=0;//自己和自己的距离为0 } } } FOR(i,m,1) { int a,b,c; sc(a); sc(b); sc(c); mp[a][b]=c; //mp[b][a]=c; 无向图 } FOR(k,n,1)//中间点 { FOR(i,n,1)//起始点 { FOR(j,n,1)//终点 { if(i!=j&&j!=k) { mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);//dp的思想 } } } } FOR(i,n,1) { FOR(j,n,1) { printf("dis %d %d =%d\n",i,j,mp[i][j]);//打印当前某两个点的最短路径 } } } return 0;}
三.Dijkstra算法
1.基本思想:
Dijkstra算法是以一个点为起点开始查询到其他点的最短路径的,其时间复杂度为O(n²),如果查询所有的点的话和Floyd算法的时间复杂度就变成一样的了,同样是O(n³),和不久前所学的最小生成树的普利姆算法有异曲同工之妙,运用的贪心的思想,利用局面最小解来解决这类问题。
Dijkstra算法按路径长度,递增次序产生最短路径。利用双重for循环去查找dis[i] 1到i的最短路径 找到后再两for循环之间添加一个更新for循环更新dis[i]的值
2.Dijkstra算法模板
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <map>#include <cmath>#include <queue>#include <string>#include <vector>#include <set>using namespace std;#define ll long long#define sc(x) scanf("%d",&x)#define dsc(x,y) scanf("%d%d",&x,&y)#define sssc(x) scanf("%s",s)#define sdsc(x,y) scanf("%s %s",x,y)#define ssc(x,y,z) scanf("%d%d%d",&x,&y,&z)#define pr(x) printf("%d\n",x)#define FOR(i,n,o) for(int i=o;i<=n;i++)#define lcr(a,b) memset(a,b,sizeof(a))#define Inf 1<<29int mp[100][100];int dis[100];int vis[100];int n,m;void dij(int s){ FOR(i,n,1) { vis[i]=0;//初始化全部未使用 dis[i]=mp[s][i];//dis数组记录1到i的最短路径 } vis[s]=1;//标记s使用过 dis[s]=0;//标记s到自己的路径为0 FOR(i,n,1) { int to=-1; int d=Inf; FOR(j,n,1) { if(!vis[j]&&d>dis[j]) { d=dis[j]; to=j; } } vis[to]=1; FOR(j,n,1) { if(!vis[j]&&dis[j]>dis[to]+mp[to][j]) dis[j]=dis[to]+mp[to][j]; } } return ;}int main(){ while(~dsc(n,m))//n个点 m条路 { FOR(i,n,1) { FOR(j,n,1) { if(i!=j) mp[i][j]=Inf;//初始化极大值 else mp[i][j]=0;//自己和自己距离为0 } } FOR(i,m,1) { int a,b,c; ssc(a,b,c); mp[a][b]=c;//无向图 mp[b][a]=c; } dij(1);//以第一个点开始 FOR(i,n,2)//打印点1到其他点的最短路径 { printf("%d %d\n",i,dis[i]); } } return 0;}
注意:上面这种方法是无法处理负权值边的,下面我要讲的算法则可以处理负权值边的,因为它加入了一种新的判断。
四.Bellman-Ford(贝尔曼)算法
1.基本思想
Dijkstra算法无法判断含负权边的图的最短路。如果遇到负权,在没有负权回路存在时(负权回路的含义是,回路的权值和为负。)即便有负权的边,也可以采用Bellman-Ford算法正确求出最短路径,。
Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图
G=(V,E),其源点为s,加权函数 w是 边集 E
的映射。对图G运行Bellman-Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s到
图G的任意顶点v的最短路径d[v]。
另外贝尔曼算法的时间复杂度为O(n*m) n个点m条边
2.Bellman-Ford(贝尔曼)算法模板
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <map>#include <cmath>#include <queue>#include <string>#include <vector>#include <set>using namespace std;#define ll long long#define sc(x) scanf("%d",&x)#define dsc(x,y) scanf("%d%d",&x,&y)#define sssc(x) scanf("%s",s)#define sdsc(x,y) scanf("%s %s",x,y)#define ssc(x,y,z) scanf("%d%d%d",&x,&y,&z)#define pr(x) printf("%d\n",x)#define FOR(i,n,o) for(int i=o;i<=n;i++)#define lcr(a,b) memset(a,b,sizeof(a))#define Inf 1<<29int mp[100][100];int dis[100];int vis[100];struct node{ int u; int v; int w;}q[105];int n,m;int bellman_floyd(int s){ FOR(i,n,1) { dis[i]=Inf;//初始化为极大值 } dis[s]=0; FOR(i,n-1,1) { for(int j=1;j<=2*m;j++) { if(dis[q[j].u]+q[j].w<dis[q[j].v]) dis[q[j].v]=dis[q[j].u]+q[j].w; } } for(int i=1;i<=2*m;i++) { if(dis[q[i].u]+q[i].w<dis[q[i].v]) return -1;//浮旋回路 } return 1;}int main(){ while(~dsc(n,m))//n个点 m条路 { int cnt=0; for(int i=1;i<=2*m;i+=2)//因为是无向图 所有要这样输入 边界也是2*m { ssc(q[i].u,q[i].v,q[i].w); q[i+1].v=q[i].u; q[i+1].u=q[i].v; q[i+1].w=q[i].w; } int x=bellman_floyd(1);//以1为起点查找和其他点的最短路径 if(x==-1) printf("no\n"); else { FOR(i,n,1) { printf("dis %d %d\n",i,dis[i]); } } } return 0;}
END!!!!!!!!!!!!!!!!!!!!!
- 最短路径算法
- 最短路径算法
- 最短路径理解
- 最短路径算法
- 最短路径算法
- 最短路径问题
- 最短路径
- 最短路径
- 最短路径算法
- 图@ 最短路径
- 最短路径
- 最短路径算法
- 最短路径
- hdu2544(最短路径)
- 最短路径问题
- 最短路径问题
- 最短路径算法
- 最短路径算法
- 工具类--DensityUtil dp与px的相互转换
- android studio2.1关联源码bug记录
- Codeforces Round #354 (Div. 2) B. Pyramid of Glasses (模拟)
- 利用python遍历目录,批处理图片
- 开服首弹
- 最短路径
- hdoj 5512 Pagodas (gcd)
- diff命令比较两个文件的不同
- Javascript基础-变量和数据类型
- 工具类--DataCleanManager 数据清除管理器
- word2vec中关于霍夫曼树的应用原理
- 轻松把玩HttpClient之封装HttpClient工具类(三),插件式配置Header
- EasyUI学习第一篇:初识EasyUI、Messager 消息框
- 工具类--FileUtils 创建文件存储路径