初识最短路,今天只弄了一个迪杰斯特拉算法,而且还没弄成熟,只会最基本的O(n^2),想弄个优先队列都发现尼玛被坑爆了,那个不应该用迪杰斯特拉算法写
表示还是不会优化版的迪杰斯特拉算法,(使用优先队列),只会普通的O(n^2);
用HDU_3790来详解这个算法吧。
Description
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
Input
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)
(1<n<=1000, 0<m<100000, s != t)
Output
输出 一行有两个数, 最短距离及其花费。
Sample Input
3 2
1 2 5 6
2 3 4 5
1 3
0 0
Sample Output
9 11
在迪杰斯特拉算法里,除了必要的建图和初始化,核心部分其实很简单。。。而且理解了搜索路径的过程,更加方便。
初始化部分
d[]数组存放当前节点到源点的最短路径,除了d[源点]=0,其余全部设置为INF
v[i][j]数组存放从i到j的路程,在读入数据之前,全部设置为INF
vis[]数组用来存放已经得到最短路径的点,用该数组的目的是为了防止该点被再次搜索。
吃完饭之后忽然想到,有一点很重要没有提醒。。。在无向图里面,读入边权值时,需要v[i][j]=v[j][i]=权值,原因很简单,无向图。
核心部分
直接贴代码讲算了
#include <iostream> #include <cstdio> #include <cstring> #define maxn 1<<30 int d[1005][1005]; int fee[1005][1005]; int distant[1005]; int p[1005]; int vis[1005]; int n,m; void dijst(int be,int en)//迪杰斯特拉算法的核心部分。 { memset(vis,0,sizeof vis);//初始化vis数组 int i,j,k; for (i=1; i<=n; i++)//初始化每个节点的最短路程和费用,在搜索前,默认都为INF { distant[i]=maxn; p[i]=maxn; } distant[be]=0;//源点的最短路径设置为0 p[be]=0; for (i=1; i<=n; i++)//遍历所有节点 { int min=maxn,minloc=0,minip=maxn; for (j=1; j<=n; j++) //找出当前还没vis过的节点中的最短路径点(第一次肯定是源点)。。由于这道题涉及费用比较,所以写得长了一些。 { if (vis[j]) continue; if (distant[j]<min) { min=distant[j]; minloc=j; minip=p[j]; } if (distant[j]==min&&min<maxn) { minloc= minip<=p[j]?minloc:j; min=distant[minloc]; minip=p[minloc]; } } vis[minloc]=1; //循环过后,minloc点必定是路径最小点,vis它一下。 for (k=1; k<=n; k++) { if (vis[k]) continue; if (d[k][minloc]==maxn) continue; if (distant[k]>distant[minloc]+d[k][minloc]) //将上面找到的“源点”依次与所有点遍历一遍,这样基于该源点往下走的所有节点的“当前最短路”全部求出来了,当然没有路劲链接的点 长度为INF,所以不会改变,也不影响结果。 { distant[k]=distant[minloc]+d[k][minloc]; p[k]=p[minloc]+fee[minloc][k]; } if (distant[k]==distant[minloc]+d[k][minloc]) { if (p[k]>p[minloc]+fee[minloc][k]) p[k]=p[minloc]+fee[minloc][k]; } } } } int main() { while (scanf("%d %d",&n,&m)&&n) { int i,j,k; for (i=1; i<=n; i++) { for (j=1; j<=n; j++)//初始化距离和费用 { d[i][j]=maxn; fee[i][j]=maxn; } } for (i=0; i<m; i++) { int a,b,c,e; scanf("%d %d %d %d",&a,&b,&c,&e); if (d[a][b]>c) //基于这个题目,这里的判断是必须的,因为根据题意,a到b可能有好几条路,而且路程允许相等,这个时候就要比较费用大小了 { d[a][b]=d[b][a]=c; fee[a][b]=fee[b][a]=e;//无向图,所以要a到b b到a都设置一下 } if (d[a][b]==c&&fee[b][a]>e) { fee[b][a]=fee[a][b]=e; } } int sta,end; scanf("%d %d",&sta,&end); dijst(sta,end); printf("%d %d ",distant[end],p[end]);//源点到任意点end的最短路径就保存在distant[]数组中,调用即可。 } return 0; }