最短路径(迪杰斯特拉Dijkstra算法与弗洛伊德floyd算法)

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值