(④)Dijkstra算法的执行步骤:
1.先初始化D[i]=v与i之间的距离(若两点不相连则为INIFY),mark[i]=0,p[i]=0,并将起始节点v的mark[v]=0;
2.遍历剩余的节点,找出剩余节点与v之间的距离(初始状态下除去相连的节点间有距离外其余所有节点间距离为INIFY),不相连的节点依然设为INIFY不变。找出其中与v距离最小的那个点k,mark[k]=1;
3.遍历所有节点,对其中mark[i]==0的点与k点的距离+2中的那个最小距离与D[i]比较,若小于D[i]则更新D[i],并将p[i]标记为k(k为该节点的前驱)。
4.遍历完后得到的D[i]就是v到各个节点的最短距离.
(⑤).Dijkstra算法的实现:
int dijkstra(int n)
{
//初始化v[0]到v[i]的距离
for(int i=1;i<=n;i++)
dis[i] = w[0][i];
vis[0]=1;//标记v[0]点
for(int i = 1; i <= n; i++)
{
//查找最近点
int min = INF,k = 0;
for(int j = 0; j <= n; j++)
if(!vis[w] && dis[j] < min)
min = dis[w],k = j;
vis[k] = 1;//标记查找到的最近点
//判断是直接v[0]连接v[j]短,还是经过v[k]连接v[j]更短
for(int j = 1; j <= n; j++)
if(!vis[j] && min+w[k][j] < dis[j])
d[j] = min+w[k][j];
}
return dis[j];
}
(⑥).例题:
①:大学经典教材<<数据结构>>(C语言版 严蔚敏 吴为民 编著) 中该算法的实现
测试数据 教科书 P189 G6 的邻接矩阵 其中 数字 1000000 代表无穷大
6
1000000 1000000 10 100000 30 100
1000000 1000000 5 1000000 1000000 1000000
1000000 1000000 1000000 50 1000000 1000000
1000000 1000000 1000000 1000000 1000000 10
1000000 1000000 1000000 20 1000000 60
1000000 1000000 1000000 1000000 1000000 1000000
结果:
D[0] D[1] D[2] D[3] D[4] D[5]
0 1000000 10 50 30 60
#include
#include
#define MAX 1000000
using namespace std;
int arcs[10][10];//邻接矩阵
int D[10];//保存最短路径长度
int p[10][10];//路径
int final[10];//若final[i] = 1则说明 顶点vi已在集合S中
int n = 0;//顶点个数
int v0 = 0;//源点
int v,w;
void ShortestPath_DIJ()
{
for (v = 0; v < n; v++) //循环 初始化
{
final[v] = 0; D[v] = arcs[v0][v];
for (w = 0; w < n; w++)
p[v][w] = 0;//设空路径
if (D[v] < MAX) {p[v][v0] = 1; p[v][v] = 1;}
}
D[v0] = 0; final[v0]=0; //初始化 v0顶点属于集合S
//开始主循环 每次求得v0到某个顶点v的最短路径 并加v到集合S中
for (int i = 1; i < n; i++)
{
int min = MAX;
for (w = 0; w < n; w++)
{
//我认为的核心过程--选点
if (!final[w]) //如果w顶点在V-S中
{
//这个过程最终选出的点 应该是选出当前V-S中与S有关联边
//且权值最小的顶点 书上描述为 当前离V0最近的点
if (D[w] < min) {v = w; min = D[w];}
}
}
final[v] = 1; //选出该点后加入到合集S中
for (w = 0; w < n; w++)//更新当前最短路径和距离
{
/*在此循环中 v为当前刚选入集合S中的点
则以点V为中间点 考察 d0v+dvw 是否小于 D[w] 如果小于 则更新
比如加进点 3 则若要考察 D[5] 是否要更新 就 判断 d(v0-v3) + d(v3-v5) 的和是否小于D[5]
*/
if (!final[w] && (min+arcs[v][w] {
D[w] = min + arcs[v][w];
// p[w] = p[v];
p[w][w] = 1; //p[w] = p[v] + [w]
}
}
}
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> arcs[i][j];
}
}
ShortestPath_DIJ();
for (int i = 0; i < n; i++) printf("D[%d] = %d\n",i,D[i]);
return 0;
}
②:Dijkstra算法---HDU 2544 水题(模板)http://acm.hdu.edu.cn/showproblem.php?pid=2544
题目大意:
搬东西很累,想省力,给你几个点和点之间的距离;标准题型;
输入:包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
测试数据:
Sample Input
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
Sample Output
#include
#include
#include
using namespace std;
#define INF 0xfffffff
int maps[110][110],dis[110],visit[110];//maps[1][2]=10为点1到点2的值为10;visit[1]=1 为1这个点已经处理
int n,m;//n为多少个路口也就是多少个点,m为多少条路也就是多少个相互连接的点
int dijstra()
{
memset(visit,0,sizeof(visit));//初始化visit的所有的值
for (int i=1;i<=n;i++)
{
dis[i]=maps[1][i];//将1点到其他所有点的值赋给dis[i];
}
visit[1]=1;//标记1点已经处理
dis[1]=0;//到自己的距离为0
for (int i=1;i<=n;i++)
{
int pos;//记录j值;
int mins=INF;//记录最小值
for (int j=1;j<=n;j++)
{
if (!visit[j]&&mins>dis[j])//j点未被处理,且j点到1点的值最小
{
pos=j;//记住j这个点
mins=dis[j];//更新最小值
}
}
visit[pos]=1;//已经处理pos这个点;
for (int j=1;j<=n;j++)
{
if (!visit[j]&&dis[j]>dis[pos]+maps[pos][j])//循环不断找出结点;
{
dis[j]=dis[pos]+maps[pos][j];
}
}
}
return dis[n];//终点在n这个地方;
}
int main()
{
int i,j;
while(~scanf("%d%d",&n,&m),n||m)
{
for(i=1;i<=n;++i)
{
for(j=1;j<=n;++j)
{
maps[i][j]=INF;
}
}
int a,b,c;//a,b都为两个相互连接的点,c为连接的距离
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&a,&b,&c);
if(c maps[a][b]=maps[b][a]=c;
}
int counts=dijstra();
printf("%d\n",counts);
}
return 0;
}
③HDU - 2112 (徐总坐公交车)
Input
输入数据有多组,每组的第一行是公交车的总数N(0<=N<=10000);
第二行有徐总的所在地start,他的目的地end;
接着有n行,每行有站名s,站名e,以及从s到e的时间整数t(0 note:一组数据中地名数不会超过150个。
如果N==-1,表示输入结束。
Output
如果徐总能到达目的地,输出最短的时间;否则,输出“-1”。
Sample Input
6
xiasha westlake
xiasha station 60
xiasha ShoppingCenterofHangZhou 30
station westlake 20
ShoppingCenterofHangZhou supermarket 10
xiasha supermarket 50
supermarket westlake 10
-1
Sample Output
50
仔细看代码可能会理解(Dijkstra算法):
#include
#include
#include
#include
#define INF 0x3ffffff
#define MAXs 170
using namespace std;
int dis[MAXs],visit[MAXs],maps[MAXs][MAXs];
char s[35],e[35],place[MAXs][35];
int counts;
void Dijkstra(int start,int endd)
{
//int pos=start;
memset(visit,0,sizeof(visit));
for(int i=1;i<=counts;i++)
dis[i]=maps[start][i];
visit[start]=1;
dis[start]=0;
for(int i=1;i<=counts;i++)
{
int mins=INF;
int pos;
for(int j=1;j<=counts;j++)
if(!visit[j]&&mins>dis[j])
{
mins=dis[j];
pos=j;
}
visit[pos]=1;
for(int j=1;j<=counts;j++)
if(!visit[j]&&dis[j]>dis[pos]+maps[pos][j])
dis[j]=dis[pos]+maps[pos][j];
}
if(dis[endd]==INF)
printf("-1\n");
else
printf("%d\n",dis[endd]);
}
int main()
{
int N;
int a,b,sign,leng;
while(~scanf("%d",&N)&&N!=-1)
{
for(int i=1;i for(int j=1;j {
if(i==j)
maps[i][j]=0;
else
maps[i][j]=INF;
}
scanf("%s%s",place[1],place[2]);//起始点
counts=2;
while(N--)
{
int leng;//两点的距离
scanf("%s%s%d",s,e,&leng);//输入并提示两点及距离
sign=0;//相当于标记变量
for(int i=1;i<=counts;i++)
if(strcmp(s,place[i])==0)//查找是否记录到数组中
{
sign=1;
a=i;//记录匹配到的位置;
break;
}
if(!sign)
{
counts++;
strcpy(place[counts],s);
a=counts;
//若没有记录入数组,则加入数组,其字符串地名对应的编号即为数组的下标,当前的counts值
}
//重复操作对比站点e
sign=0;//需要更新sign的值
for(int i=1;i<=counts;i++)
if(strcmp(e,place[i])==0)
{
sign=1;
b=i;
break;
}
if(!sign)
{
counts++;
strcpy(place[counts],e);
b=counts;
}
if(maps[a][b]>leng)
maps[a][b]=maps[b][a]=leng;
}
if(strcmp(place[1],place[2])==0)
printf("0\n");
else
Dijkstra(1,2);
}
return 0;
}
解题思路:这个题是要求输入起点与终点的,难点是对字符串的处理。开始会对maps[][]进行初始化,每行输入的站点与之间的距离要全部放入到maps这个数组中,然后用Dijkstra算法进行处理。
接下来是SPFA算法的代码:
#include
#include
#include
#include
#include
理解:
相比于上边Dijkstra算法,用到map()函数确实对于字符串的处理有了很方便的地方,接下来的SPFA算法基本上就是在套用模板,但是还是要注意一些细节,一些变量的初始化。
还有就是counts++与++counts问题:
记住是先传递还是后传递,例如a=counts++是先把值counts原先的值赋给a,接着在counts加一,
a=++counts是先counts加一,在把counts赋给a;
例:
counts=1;
a=counts++;
b=counts;这样:a=1,b=2;
若a=++counts;这样a=2,b=2;不要太计较其他的就可以了;
还有就是即将要讲的Floyd算法代码:
#include
#include
#include
#include
#include
学完Floyd算法看到那简短的代码,就直到压缩的全是精华了,所以记住模板差不多就可以:
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(maps[i][k]!=INF&&maps[k][j]!=INF)
maps[i][j]=min(maps[i][k]+maps[k][j],maps[i][j]);
//Floyd算法模板
其他的地方和上边的算法差不多,。多多理解。
(四)Floyd算法
①:理解
Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。
算法思想原理:
Floyd算法是一个经典的动态规划算法,稠密图效果最佳,边权可正可负。此算法简单有效,由于三重循环结构紧凑,对于稠密图,效率要高于执行|V|次Dijkstra算法,也要高于执行|V|次SPFA算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
算法描述:
a.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
b.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。
②:基本代码
很简单吧,代码看起来可能像下面这样:
- for ( int i = 0; i < 节点个数; ++i )
- {
- for ( int j = 0; j < 节点个数; ++j )
- {
- for ( int k = 0; k < 节点个数; ++k )
- {
- if ( Dis[i][k] + Dis[k][j] < Dis[i][j] )
- {
-
- Dis[i][j] = Dis[i][k] + Dis[k][j];
- }
- }
- }
- }
但是这里我们要注意循环的嵌套顺序,如果把检查所有节点X放在最内层,那么结果将是不正确的,为什么呢?因为这样便过早的把i到j的最短路径确定下来了,而当后面存在更短的路径时,已经不再会更新了。
让我们来看一个例子,看下图:
图中红色的数字代表边的权重。如果我们在最内层检查所有节点X,那么对于A->B,我们只能发现一条路径,就是A->B,路径距离为9。而这显然是不正确的,真实的最短路径是A->D->C->B,路径距离为6。造成错误的原因就是我们把检查所有节点X放在最内层,造成过早的把A到B的最短路径确定下来了,当确定A->B的最短路径时Dis(AC)尚未被计算。所以,我们需要改写循环顺序,如下:
- for ( int k = 0; k < 节点个数; ++k )
- {
- for ( int i = 0; i < 节点个数; ++i )
- {
- for ( int j = 0; j < 节点个数; ++j )
- {
- if ( Dis[i][k] + Dis[k][j] < Dis[i][j] )
- {
-
- Dis[i][j] = Dis[i][k] + Dis[k][j];
- }
- }
- }
- }
毕竟这个算法比较简单加上上边也有有关例题,所以再找一个简单的例题!!
poj 3615 Cow Hurdles
题意:
给你一个有向图,然后对于特定的点A与B,要你求出A到B之间所有可行路径的单段路距离最大值的最小值.(有点绕)
分析:
剥除floyd的外壳,本题d[i][j]表示的是从i到j的所有路径中单段路距离最大值的最小值。所以其实本题还是依据动态规划的思想来做的,
当不同类d[i][k]和d[k][j]合并的时候,取得是max(最大值,因为我们要求i到j这段路内的最大值)。然后当同类路径值(即两个d[i][j]可行值)选优时,取得是最小值(因为我们要求所有i到j路径目的值中的最小值)。
代码:
#include
#include
#define INF 1e9
using namespace std;
const int maxn = 300+10;
int n,m,t;
int d[maxn][maxn];
void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(d[i][k] d[i][j]=min(d[i][j],max(d[i][k],d[k][j]));
}
int main()
{
while(scanf("%d%d%d",&n,&m,&t)==3)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]= i==j?0:INF;
for(int i=1;i<=m;i++)
{
int u,v,height;
scanf("%d%d%d",&u,&v,&height);
d[u][v]=height;
}
floyd();
while(t--)
{
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",d[u][v]==INF?-1:d[u][v]);
}
}
return 0;
}
(五)知识点:
具体map函数讲解:http://blog.csdn.net/pfl_327/article/details/77188385
map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
map的基本操作函数
C++ Maps是一种关联式容器,包含“关键字/值”对
begin() 返回指向map头部的迭代器
clear() 删除所有元素
count() 返回指定元素出现的次数
empty() 如果map为空则返回true
end() 返回指向map末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
size() 返回map中元素的个数
swap() 交换两个map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数