路径寻找问题可以归结为隐式图的遍历,它的任务是找到一条从初始状态到终止状态的最优路径,而不是像回溯法那样找到一个符合某些要求的解。
图的存储方式:邻接矩阵、邻接表、十字链表、邻接多重表等。这里介绍邻接矩阵。
参见百度百科:
邻接矩阵是表示顶点之间相邻关系的矩阵。
设G=(V,E)是一个有向图,其逻辑结构分为两部分,V和E集合。因为我们可以用一个二维数组存放顶点与顶点的权值,数组下标表示顶点。
如:
给定一个图:
则该图的邻接矩阵为:
| 1 | 2 | 3 | 4 |
1 | 0 | 2 | 6 | 4 |
2 | INF | 0 | 3 | INF |
3 | 7 | INF | 0 | 1 |
4 | 5 | INF | 12 | 0 |
什么是最短路?
假如我们要从2走到4,如图,我们有两种路径:
第一种:2——>3——>4 总权值(路径)为:4
第二种:2——>3——>1——>4 走权值(路径)为:14
所以第一种为最短路;
弗洛伊德算法(适用多源最短路径问题):
因为两个顶点之间不一定有直接连通且权值最小的路,所以往往要经过其顶点的中转而到达目标顶点。
比如:
当我们要求所有的顶点都必须经过顶点一时再求其最短路,我们就需要比较e[i][1]+e[1][i]与e[i][j]大小;
void solve()
{
for(int i=1;i<=V;i++)
for(int j=1;j<=V;j++)
e[i][j]=min(e[i][1]+e[1][j],e[i][j]);
}
之后我们的邻接矩阵变为:
可以发现,有些顶点之间的权值变小了,比如说原本3——>2之间的权值为极大值,也就是3!——>2,现在3通过1中转至2,权值缩小为9,,所以弗洛伊德算法就是遍历整个图,保存每个顶点之间的最短路,时间复杂度o(n^3)
所以求整个图的最短路代码为:
void Floyd_Warshall()
{
for(int k=1;k<=V;k++)
for(int i=1;i<=V;i++)
for(int j=1;j<=V;j++)
e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
}
Dijkstra算法(单源最短路径问题)
迪杰斯特拉算法是计算一个点(注意是某一个顶点)到其他所有点的最短路径,特点是采用广度优先搜索的思想,以起始点为中心,向外层层扩散,直到扩散到终点。
算法思路:
1.通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。
2.此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
3.初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是”起点s到该顶点的路径”。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 … 重复该操作,直到遍历完所有顶点。
操作步骤:
1.首先指定一个起始点s;
2.定义一个二维数组e[MAXN][MAXN]和两个一维数组dis[MAXN],book[MAXN],e的作用保存图的信息,dis的作用是存储起始点到其他点的最短路径,book的作用是判断某个点是否被遍历过。
3.首先给dis数组赋值,设起始点为s,则dis[s]=0,因为到自己的距离为0,其他的,若起始点与该顶点之间有边,则赋值为该边(权值)大小,否则赋值为INF;
4.再book数组中选择一个距离顶点s最近的顶点u,存入dis数组中,然后遍历所有点u能直接到达的点(设为s1),并对每一条边进行判断,如果dis[s1]>dis[u]+e[u][s1],则dis[s1]=dis[u]+e[u][s1],一直重复此操作,直到所有点遍历完毕;
#define MAXN 60
#define INF 999999
int e[MAXN][MAXN];
bool book[MAXN]={false};
int dis[MAXN];
int V,E;
void prepare()
{
//对邻接矩阵初始化
for(int i=0;i<MAXN;i++)
for(int j=0;j<MAXN;j++)
if(i==j)
e[i][j]=0;
else
e[i][j]=INF;
//输入
int a,b,c;
cin >> V >> E;
for(int i=0;i<E;i++)
{
cin >> a >> b >> c;
e[a][b]=c; //从a到b的权值为c
}
//初始化dis数组
for(int i=1;i<=V;i++)
dis[i]=e[1][i];
book[1]=true;
}
void Dijkstra()
{
int u;
int minn=INF;
for(int i=1;i<=V-1;i++)
{
minn=INF;
//找距离1号最近的顶点
for(int j=1;j<=V;j++)
{
if(book[j]==false&&dis[j]<minn)
{
minn=dis[j];
u=j;
}
}
book[u]=true;
//松弛
for(int k=1;k<=V;k++)
{
if(e[u][k]<INF)
dis[k]=min(dis[k],dis[u]+e[u][k]);
}
}
}