最短路径:
给定连通有向图
G
及其中的顶点
u
和
v,
找到从
u
到
v
的最短路径及其长度。
无权图的最短路径:两点间边数最少(BFS)
有向带权图:加起来权重最小,分为单源最短路径、每对点最短路径。
Dijkstra算法(解决单源最短路径)
条件:每条边的权重非负
单源最短路径:给定源顶点s,求它到其他任意顶点的最短路径
算法:
s:已求出最短路径的顶点集合
L=V-S;每个步骤从L选取一个顶点v加入S
贪心准则:v是L中距s距离最短者;新最短路径=已有最短路径+一条边
每个顶点无需保存其完整路径,保存路径中它的前一顶点即可实现:
数组p:保存路径(p[i]:最短路径中,顶点i的前驱顶点)数组d:当前最短路径长度
i在s中:真正的最短路径
i在L中:当前的最短路径
代码:
#include<iostream>
#include<stack>
#include<vector>
#define INF 0x3f3f3f3f
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
int(*path)[20] = new int[20][20];
int* dist = new int[20];//d[i]为从root到i的最短距离
int* visit = new int[20];//i是否已经访问
vector<int> pre(20);//记录前一个点
for (int i = 0; i < n; i++)
pre[i] = i;
while (m--)
{
int i, j, k;
cin >> i >> j >> k;
path[i][j] = k;
}
int root;
cin >> root;
memset(dist, INF, sizeof(dist));
dist[root] = 0;
memset(visit, 0, sizeof(visit));
//主要算法区域
for (int i = 1; i <= n; i++)
{
int j = 0;//遍历,找到当前距地最短的点j
for (int k = 1; k <= n; k++)
{
if (!visit[k] && dist[k] <= dist[j])
j = k;
}
visit[j] = 1;
for (int k = 1; k <= n; k++)
{
if (dist[k] > dist[j] + path[j][k])
{
dist[k] = dist[j] + path[j][k];
pre[k] = j;
}
//以j为起点更新附近的点
}
}
for (int i = 1; i < n; i++)
cout << dist[i] << " ";
}
Floyd算法(每一对点的最短路径)
顶点编号为 1 ~ nc(i,j,k) : i-> j 的“最短路径”长度 —— 加了限制条件,路径中顶点的最大编号为 k(除开起终点)存在边<i, j> 则 c(i, j, 0)=<i, j> 的长度不存在边<i, j>,则 c(i, j, 0)=+ ∞c(i, i, 0)=0;c(i, j, n)——最短路径长度
计算c(i,j,k):
路径中无k: c1(i,j,k)=c(i,j,k-1)
路径中有k: c2(i,j,k)=c(i,k,k-1)+c(k,j,k-1) 即两段的最小路径
于是:c(i,j,k)=min{c1,c2}
复杂度分析:递归算法 O(n*2^n); 迭代计算 O(n^3)
OJ-(floyd算法实现)
在oj基础上实现了打印经过点
#include<iostream>
#include<cstring>
#include <time.h>
#define inf 0x3f3f3f3f
using namespace std;
//打印路径
void print_pass(int pass[20][20],int i, int j)
{
if (i == j)return;
if (pass[i][j] == -1)
cout << i << "->";
//(pass=-1即中间没经过点)
else//递归
{
print_pass(pass, i, pass[i][j]);
print_pass(pass,pass[i][j],j );
}
}
int main()
{
int n, m;
cin >> n >> m;
//初始化
int(*num)[20] = new int[20][20];//(储存路径)
int(*pass)[20] = new int[20][20];//储存经过的最大数
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
pass[i][j] = -1;
if (j == i)
num[i][j] = 0;
else
num[i][j] = inf;
}
}
for (int i = 0; i < m; i++)
{
int x, y, k;
cin >> x >> y >> k;
num[x][y] = k;
}
//主体部分如下!
for (int k = 0; k < n; k++)//中间能经过的最大数
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (num[i][j]>(num[i][k] + num[k][j]))
{
num[i][j] = num[i][k] + num[k][j];
pass[i][j] = k;//最后的k是最大的k
}
}
}
}
cout << endl;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (num[i][j] == inf)
cout << -1 << " ";
else
{
cout << num[i][j] << " ";
print_pass(pass, i, j);
cout << j << endl;
}
}
}
return 0;
}