Dijkstra算法
形式上与生成最小树的普里姆Prim算法有在形式上有异曲同工之妙,它可以找到有权图的任意顶点到其它所有顶点的最短路径。采用了贪心思想。仅查找某个起点时间复杂度为O(n^2).
代码如下:
vector<vector<int>> edges; //邻接矩阵
int N//顶点数
vector<int> shortPath;//储存起点到其所有顶点的最短权值和
vector<int> direction;//储存的值为该下标的前驱
void Dijkstra(int s)//得到以s为起点其他任意顶点为终点的所有最短路径
{
shortPath = edges[s];
direction.resize(N);
fill(direction.begin(), direction.end(), s);
vector<bool> final(N, 0);//判断起点是否找到以下标为编号的顶点之间的最短权值
final[s] = 1;//起点加入
shortPath[s] = 0;//起点与起点之间的距离为0
for (int i = 1; i < N; ++i)
{
int Min = INT_MAX, cur = 0;
for (int j = 0; j < N; ++j)
{
if (!final[j] && shortPath < Min)
{
Min = shortPath[j];
cur = j;
}
}
}
//此时找到的min就是起点s到cur之前的最小权值
final[cur] = 1;//cur不用再找
for (int j = 0; j < N; ++j)
{
if (!final[j] && Min < shortPath[j] - edges[cur][j])
{/*Min + edges[cur][j] < shortPath[j] 如果起点到cur的权值+cur与j边的权值 < 已知起点到j的权值 就更新shortPath[j]备用*/
shortPath = Min + edges[cur][j];
direction[j] = cur;//更新j的前驱为cur
}
}
}
最后的得到的shortPath与direction,shortPath[3] = 10表示起点到编号为3的最短距离为10.假如direction为[0,0,1,4,2,4,3,6,7]且起点为v0,direction[3] = 1表示v3->v1,direction[1] = 0表示v1->v0,所以得v0->v1->v3.
弗洛伊德算法
弗洛伊德算法代码十分简单,主要思想就是某个顶点v0为中间点,与其他所有任意两点v1,v2,判断D(v1,v2)与D(v1,v0),D(v0,v2)的大小,该算法适用于需要查找所有顶点到其他任意顶点的最短路径。时间复杂度为O(n^3)。
代码如下
vector<vector<int>> edges; //邻接矩阵
vector<vector<int>> D;//D[i][j]表示i与j之间的最短路径
int N//顶点数
void floyd()
{
D.resize(N);
for (int i = 0; i < N; ++i)
{
D[i] = edges[i];
}
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < N; ++j)
{
for (int k = 0; k < N; ++k)
{
if (D[j][k] - D[j][i] > D[i][k])
{/*D[j][k] > D[j][i] + D[i][k] */
D[j][k] = D[j][i] + D[i][k];
}
}
}
}
}
注意:Dijkstra与floyd算法对有向图也是有效的。
例题1
7-9 旅游规划(25分)
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。
输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40
Dijkstra算法
#include <bits/stdc++.h>
using namespace std;
int N, E, S, D;
vector<vector<int>> edges;
vector<vector<int>> cost;
vector<int> shortPath;
vector<int> minCost;
void Dijkstra()
{
vector<bool> final(N, S);
shortPath = edges[S];
minCost = cost[S];
final[S] = 1;
shortPath[S] = 0;
for (int i = 1; i < N; ++i)
{
int min = INT_MAX, cur;
for (int j = 0; j < N; ++j)
{
if (!final[j] && shortPath[j] < min)
{
min = shortPath[j];
cur = j;
}
}
final[cur] = 1;
for (int j = 0; j < N; ++j)
{
if (!final[j] && edges[cur][j] <= shortPath[j] - min)
{
if (edges[cur][j] + min == shortPath[j] && minCost[cur] + cost[cur][j] >= minCost[j])
{
continue;
}
shortPath[j] = edges[cur][j] + min;
minCost[j] = minCost[cur] + cost[cur][j];
}
}
}
}
int main()
{
cin>>N>>E>>S>>D;
edges.resize(N);
cost.resize(N);
for (int i = 0; i < N; ++i)
{
vector<int> tmp(N, INT_MAX);
edges[i] = tmp;
cost[i] = tmp;
}
for (int i = 0; i < E; ++i)
{
int v1, v2, x, y;
cin>>v1>>v2>>x>>y;
edges[v1][v2] = x;
edges[v2][v1] = x;
cost[v1][v2] = y;
cost[v2][v1] = y;
}
Dijkstra();
cout<<shortPath[D]<<" "<<minCost[D];
return 0;
}
例题二
7-8 哈利·波特的考试 (25分)
哈利·波特要考试了,他需要你的帮助。这门课学的是用魔咒将一种动物变成另一种动物的本事。例如将猫变成老鼠的魔咒是haha,将老鼠变成鱼的魔咒是hehe等等。反方向变化的魔咒就是简单地将原来的魔咒倒过来念,例如ahah可以将老鼠变成猫。另外,如果想把猫变成鱼,可以通过念一个直接魔咒lalala,也可以将猫变老鼠、老鼠变鱼的魔咒连起来念:hahahehe。
现在哈利·波特的手里有一本教材,里面列出了所有的变形魔咒和能变的动物。老师允许他自己带一只动物去考场,要考察他把这只动物变成任意一只指定动物的本事。于是他来问你:带什么动物去可以让最难变的那种动物(即该动物变为哈利·波特自己带去的动物所需要的魔咒最长)需要的魔咒最短?例如:如果只有猫、鼠、鱼,则显然哈利·波特应该带鼠去,因为鼠变成另外两种动物都只需要念4个字符;而如果带猫去,则至少需要念6个字符才能把猫变成鱼;同理,带鱼去也不是最好的选择。
输入格式:
输入说明:输入第1行给出两个正整数N (≤100)和M,其中N是考试涉及的动物总数,M是用于直接变形的魔咒条数。为简单起见,我们将动物按1~N编号。随后M行,每行给出了3个正整数,分别是两种动物的编号、以及它们之间变形需要的魔咒的长度(≤100),数字之间用空格分隔。
输出格式:
输出哈利·波特应该带去考场的动物的编号、以及最长的变形魔咒的长度,中间以空格分隔。如果只带1只动物是不可能完成所有变形要求的,则输出0。如果有若干只动物都可以备选,则输出编号最小的那只。
输入样例:
6 11
3 4 70
1 2 1
5 4 50
2 6 50
5 6 60
1 3 70
4 6 60
3 6 80
5 1 100
2 4 60
5 2 80
输出样例:
4 70
floyd解法
# include <bits/stdc++.h>
using namespace std;
vector<vector<int>> edges;
vector<vector<int>> D;
int N, E;
void floyd()
{
D.resize(N+1);
for (int i = 1; i <= N; ++i)
{
D[i] = edges[i];
}
for (int i = 1; i <= N; ++i)
{
for (int j = 1; j <= N; ++j)
{
for (int k = 1; k <= N; ++k)
{
if (D[j][k] - D[j][i] > D[i][k])
{
D[j][k] = D[j][i] + D[i][k];
}
}
}
}
}
int main()
{
cin>>N>>E;
edges.resize(N+1);
for (int i = 1; i <= N; ++i)
{
vector<int> tmp(N+1, INT_MAX);
edges[i] = tmp;
edges[i][i] = 0;
}
for (int i = 1; i <= E; ++i)
{
int v1, v2, w;
cin>>v1>>v2>>w;
edges[v1][v2] = w;
edges[v2][v1] = w;
}
floyd();
int id = 1, ans = INT_MAX;
for (int i = 1; i <= N; ++i)
{
int m = 0;
for (int j = 1; j <= N; ++j)
{
m = max(m, D[i][j]);
}
if (m == INT_MAX)
{
cout<<0;
return 0;
}
if((m < ans) || (m == ans && i < id))
{
ans = m;
id = i;
}
}
cout<<id<<" "<<ans;
return 0;
}