最短路径
1. 理解
- 以前数学建模的时候涉及过一点图论的东西,那时候刚接触最短路径简直头大,现在倒是觉得简单很多,其思想与最小生成树很相似。
- 最小生成树能够保证整个拓扑图的所有路径之和最小,最短路径是保证两点间路径最小,乍一看还是很相似,区别在于MST是全局最优,而最短路径是局部最优,但又不能这么说,因为MST针对的是所有点,而最短路径只针对两个点(其中涉及其他点的连接)。
- 类比MST,常用算法是从点入手找边的Dijkstra和从边入手找点的Floyd。
2. Dijkstra
- 主要原理上和prim算法的区别在于Dijkstra可能针对有向图,因此不同方向的边权可能不同。另一方面在于“最低权值”的定义不同,Prim的“最低权值”是相对于任意一点而言的,也就是把U中的点看成一个整体,每次寻找V-U中跟U的距离最小的一点加入U;而Dijkstra的“权值最低”是相对于出发点而言的,也就是每次寻找V-U中跟v0的距离最小的一点加入U。其实基本上是一样的,只是有细微的差别。
- 代码
//这个函数只是为了方便显示路径,用来回溯路径的迭代方法
void fin(vector<int>* ppath, int index)
{
vector<int> path = *ppath;
if (path[index] == 0)
cout << "V1->"<<"V"<<index+1;
else if (path[index] == -1)
cout <<"V1->V" <<index+1;
else
{
fin(ppath, path[index]);
cout <<"->V" << index+1;
}
}
int main()
{
//赋值
vector<vector<int>> weight = {
vector<int>{1,3,10},
vector<int>{1,5,30},
vector<int>{1,6,100},
vector<int>{2,3,5},
vector<int>{3,4,50},
vector<int>{4,6,10},
vector<int>{5,6,60},
vector<int>{5,4,20},
};
int pointnum = 6;
vector<vector<int>> graph(pointnum);
for (int i = 0; i <pointnum; i++)
{
vector<int> e(pointnum);
for (int j = 0; j < pointnum; j++)
{
if (i == j)
e[j] = 0;
else
e[j] = INT16_MAX;
}
graph[i] = e;
}
for (int k = 0; k < weight.size(); k++)
{
graph[weight[k][0]-1][weight[k][1]-1] = weight[k][2];
}
//两点间最短路径长度
vector<int> d(pointnum);
vector<int> v(pointnum);
//距离索引点最近的下一个点索引号
vector<int> p(pointnum,-1);
for (int i = 1; i < pointnum; i++)
{
d[i] = INT16_MAX;
}
for (int i = 0; i < pointnum; i++)
{
int x;
int m = INT16_MAX;
for (int j = 0; j < pointnum; j++)
{
if (v[j]==0 && d[j] <= m)
{
m = d[j];
x = j;
}
}
v[x] = 1;
for (int j = 0; j < pointnum; j++)
{
if (d[x] + graph[x][j] < d[j])
{
d[j] = d[x] + graph[x][j];
p[j] = x;
}
}
}
for (int i = 0; i < pointnum; i++)
{
fin(&p, i);
cout << " 最短路径为" << d[i] << endl;
}
return 0;
}
3. Floyd
- 主要原理上…额,啥玩意儿,这东西也算个算法?不就是遍历对比所有路径长度么?不过输出路径的时候倒是觉得麻烦了许多。
- 代码
vector<vector<int>> p;
void fin(int fr, int to)
{
if (p[fr][to] == 0)
cout << "V" << fr + 1 << "->V" << to + 1;
else if (p[fr][to] == -1)
cout << "V" << fr + 1 << "->V" << to + 1 ;
else
{
fin(fr, p[fr][to]);
cout << "->";
fin(p[fr][to], to);
}
}
int main()
{
vector<vector<int>> weight = {
vector<int>{1,3,10},
vector<int>{1,5,30},
vector<int>{1,6,100},
vector<int>{2,3,5},
vector<int>{3,4,50},
vector<int>{4,6,10},
vector<int>{5,6,60},
vector<int>{5,4,20},
};
int pointnum = 6;
p.resize(pointnum);
for (int i = 0; i < pointnum; i++)
{
vector<int> path(pointnum);
for (int j = 0; j < pointnum; j++)
{
path[j] = -1;
}
p[i] = path;
}
vector<vector<int>> graph(pointnum);
for (int i = 0; i <pointnum; i++)
{
vector<int> e(pointnum);
for (int j = 0; j < pointnum; j++)
{
if (i == j)
e[j] = 0;
else
e[j] = INT16_MAX;
}
graph[i] = e;
}
for (int k = 0; k < weight.size(); k++)
{
graph[weight[k][0]-1][weight[k][1]-1] = weight[k][2];
p[weight[k][0] - 1][weight[k][1] - 1] = 0;
}
for (int k = 0; k < pointnum; k++)
{
for (int i = 0; i < pointnum; i++)
{
for (int j = 0; j < pointnum; j++)
{
if (graph[i][j] > graph[i][k] + graph[k][j])
{
graph[i][j] = graph[i][k] + graph[k][j];
p[i][j] = k;
}
}
}
}
//输出最终的结果
for (int i = 0; i < pointnum; i++)
{
fin(0,i);
cout << " 最短路径为" << graph[0][i] << endl;
}
return 0;
}