最短路径wt

文章介绍了Floyd算法和迪杰斯特拉算法在解决图中两点间最短路径问题的应用。Floyd算法通过动态规划逐步构建矩阵来找到所有顶点间的最短路径,而Dijkstra算法则是从一个起点出发,逐步扩展找到最短路径。文章提供了两个算法的代码实现,并讨论了它们的特点和适用场景。

1.弗洛伊德(Floyd )

用来求解赋权图中每对顶点间的最短距离,Floyd算法是经典的动态规划算法,基本思想是递推产生一个矩阵序列A1,A2,…,Ak,…,An(图有n个节点),Ak=(ak(i,j))nxn。其中矩阵Ak第i行第j列表示从顶点vi到顶点vj的路径上经过的顶点序号不大于k的最短路径长度。

电车

题目描述

在一个神奇的小镇上有着一个特别的电车网络,它由一些路口和轨道组成,每个路口都连接着若干个轨道,每个轨道都通向一个路口(不排除有的观光轨道转一圈后返回路口的可能)。在每个路口,都有一个开关决定着出去的轨道,每个开关都有一个默认的状态,每辆电车行驶到路口之后,只能从开关所指向的轨道出去,如果电车司机想走另一个轨道,他就必须下车切换开关的状态。

为了行驶向目标地点,电车司机不得不经常下车来切换开关,于是,他们想请你写一个程序,计算一辆从路口 A A A 到路口 B B B 最少需要下车切换几次开关。

输入格式

第一行有 3 3 3 个整数 N , A , B N,A,B N,A,B( 2 ≤ N ≤ 100 , 1 ≤ A , B ≤ N 2 \leq N \leq 100, 1 \leq A,B \leq N 2≤N≤100,1≤A,B≤N),分别表示路口的数量,和电车的起点,终点。

接下来有 N N N 行,每行的开头有一个数字 K i K_i Ki​( 0 ≤ K i ≤ N − 1 0 \leq K_i \leq N-1 0≤Ki​≤N−1),表示这个路口与 K i K_i Ki​ 条轨道相连,接下来有 K i K_i Ki​ 个数字表示每条轨道所通向的路口,开关默认指向第一个数字表示的轨道。

输出格式

输出文件只有一个数字,表示从 A A A 到 B B B 所需的最少的切换开关次数,若无法从 A A A 前往 B B B,输出 − 1 -1 −1。

样例 #1

样例输入 #1

3 2 1

2 2 3

2 3 1

2 1 2

1

2

3

4

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
int n, s, e, m, x, f[1001][1001];
void floy()
{
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (!(i == j || i == k || j == k))
{
f[i][j] = min(f[i][k] + f[k][j], f[i][j]);
}
}
}
}
}
int main()
{
memset(f, INF, sizeof(f));
cin>>n>>s>>e;
for (int i = 1; i <= n; i++)
{
f[i][i] = 0;
}
for (int i = 1; i <= n; i++)
{
cin >> m;
for (int j = 1; j <= m; j++)
{
cin >> x;
if (j == 1)
{
f[i][x] = 0;
}
else
{
f[i][x] = 1;
}
}
}
floy();
if (f[s][e] == INF)
{
cout << "-1";
}
else
{
cout<<f[s][e];
}
return 0;
}

样例输出 #1

0

1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

通过中间点k求得是从i —> j近还是从i —> k —> j 近,从而求得点a到点b的最短路径(a和b都是随机点)可以把一个路口看作一张图中的一个点,轨道是图中的边(注意:这是有向图),每一条边的权值就是这个边所联通的点是否需要按按钮(需要按按钮就是1,不需要按按钮就是0)然后就用求最短路径的算法算出最少需要按的开关数。

2.迪杰斯特拉(dijkstra)

在已知图的邻接矩阵的条件下,通过遍历已知图的所有路径,用 dis[i] 数组来记录到i点的最短路径,然后在循环中不断判断更替。首先,将所有点的集合分为俩部分,一边是已经遍历过的,另外一边是没有遍历过的,分别用mark[i]=1、mark[i]=0来表示。层层扩散,实时比较和更新最短路径。

以起始点为中心向外层层扩展,直到扩展到终点为止。由for循环可知,其时间复杂度是O(n^2)。

【模板】单源最短路径(弱化版)

题目背景

本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779。

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入格式

第一行包含三个整数 n , m , s n,m,s n,m,s,分别表示点的个数、有向边的个数、出发点的编号。

接下来 m m m 行每行包含三个整数 u , v , w u,v,w u,v,w,表示一条 u → v u \to v u→v 的,长度为 w w w 的边。

输出格式

输出一行 n n n 个整数,第 i i i 个表示 s s s 到第 i i i 个点的最短路径,若不能到达则输出 2 31 − 1 2^{31}-1 231−1。

样例 #1

样例输入 #1

4 6 1

1 2 2

2 3 2

2 4 1

1 3 5

3 4 3

1 4 4

1

2

3

4

5

6

7

样例输出 #1

0 2 4 3

1

提示

【数据范围】

对于 20 % 20\% 20% 的数据: 1 ≤ n ≤ 5 1\le n \le 5 1≤n≤5, 1 ≤ m ≤ 15 1\le m \le 15 1≤m≤15;

对于 40 % 40\% 40% 的数据: 1 ≤ n ≤ 100 1\le n \le 100 1≤n≤100, 1 ≤ m ≤ 1 0 4 1\le m \le 10^4 1≤m≤104;

对于 70 % 70\% 70% 的数据: 1 ≤ n ≤ 1000 1\le n \le 1000 1≤n≤1000, 1 ≤ m ≤ 1 0 5 1\le m \le 10^5 1≤m≤105;

对于 100 % 100\% 100% 的数据: 1 ≤ n ≤ 1 0 4 1 \le n \le 10^4 1≤n≤104, 1 ≤ m ≤ 5 × 1 0 5 1\le m \le 5\times 10^5 1≤m≤5×105, 1 ≤ u , v ≤ n 1\le u,v\le n 1≤u,v≤n, w ≥ 0 w\ge 0 w≥0, ∑ w < 2 31 \sum w< 2^{31} ∑w<231,保证数据随机。

Update 2022/07/29:两个点之间可能有多条边,敬请注意。

对于真正 100 % 100\% 100% 的数据,请移步 P4779。请注意,该题与本题数据范围略有不同。

样例说明:

图片1到3和1到4的文字位置调换

#include<bits/stdc++.h>
using namespace std;
#define maxn 10005
#define maxm 500005
#define INF  1234567890
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
struct Edge
{
    int u,v,w,next;
}e[maxm];
int head[maxn],cnt,n,m,s,vis[maxn],dis[maxn];
struct node
{
    int w,now;
    inline bool operator <(const node &x)const
    //重载运算符把最小的元素放在堆顶(大根堆)
    {
        return w>x.w;//这里注意符号要为'>'
    }
};
priority_queue<node>q;
//优先队列,其实这里一般使用一个pair,但为了方便理解所以用的结构体
inline void add(int u,int v,int w)
{
    e[++cnt].u=u;
    //这句话对于此题不需要,但在缩点之类的问题还是有用的
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    //存储该点的下一条边
    head[u]=cnt;
    //更新目前该点的最后一条边(就是这一条边)
}
//链式前向星加边
void dijkstra()
{
    for(int i=1;i<=n;i++)
    {
        dis[i]=INF;
    }
    dis[s]=0;
    //赋初值
    q.push((node){0,s});
    while(!q.empty())
    //堆为空即为所有点都更新
    {
        node x=q.top();
        q.pop();
        int u=x.now;
        //记录堆顶(堆内最小的边)并将其弹出
        if(vis[u]) continue; 
        //没有遍历过才需要遍历
        vis[u]=1;
        for(int i=head[u];i;i=e[i].next)
        //搜索堆顶所有连边
        {
            int v=e[i].v;
            if(dis[v]>dis[u]+e[i].w)
            {
            dis[v]=dis[u]+e[i].w;
            //松弛操作
            q.push((node){dis[v],v});
            //把新遍历到的点加入堆中
            }
        }
    }
}
int main()
{
    n=read(),m=read(),s=read();
    for(int i=1,x,y,z;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        add(x,y,z);
    }
    dijkstra();
    for(int i=1;i<=n;i++)
    {
        printf("%d ",dis[i]);
    }
    return 0;
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

首先用数组dis记录起点到每个结点的最短路径,再用一个数组保存已经找到最短路径的点

然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点记为已经找到最短路

此时完成一个顶点,再看这个点能否到达其它点(记为v),将dis[v]的值进行更新

不断重复上述动作,将所有的点都更新到最短路径

迪杰斯特拉算法:

迪杰斯特拉算法是一种用于求解最短路径的算法,它可以在一个有向图中求出从一个顶点到其他所有顶点的最短路径。它的基本思想是:从源点出发,每次选择距离源点最近的顶点,更新源点到其他顶点的最短路径。

弗洛伊德算法:

弗洛伊德算法是一种用于求解最短路径的算法,它可以在一个有向图中求出从一个顶点到其他所有顶点的最短路径。它的基本思想是:从源点出发,每次选择距离源点最近的顶点,更新源点到其他顶点的最短路径。它的优点是可以处理负权边,但是它的时间复杂度是O(n^3)。

Bellman-Ford算法:

Bellman-Ford算法是一种用于求解最短路径的算法,它可以在一个有向图中求出从一个顶点到其他所有顶点的最短路径。它的基本思想是:从源点出发,每次选择距离源点最近的顶点,更新源点到其他顶点的最短路径。它的优点是可以处理负权边,而且它的时间复杂度是O(mn),其中m是边的数量,n是顶点的数量。

典型最短路径算法,用于计算一个节点到其他节点的最短路径。

基本原理:逐遍的对图中每一个边去迭代计算起始点到其余各点的最短路径,执行N-1遍,最终得到起始点到其余各点的最短路径。(N为连通图结点数)

SPFA算法:

SPFA算法是一种用于求解最短路径的算法,它可以在一个有向图中求出从一个顶点到其他所有顶点的最短路径。它的基本思想是:从源点出发,每次选择距离源点最近的顶点,更新源点到其他顶点的最短路径。它的优点是可以处理负权边,而且它的时间复杂度是O(mn),其中m是边的数量,n是顶点的数量。它的缺点是它可能会出现负环,所以在使用SPFA算法之前,需要先检测图中是否存在负环。

spfa跑的比较kuai

### 什么是旅行商问题 (TSP) 旅行商问题(Traveling Salesman Problem, TSP)是一个经典的组合优化问题,其目标是在给定一组城市及其两两之间的距离的情况下,找到一条最短路径使得旅行商能够访问每一个城市恰好一次并返回起点[^1]。 --- ### 使用遗传算法(GA)求解 TSP 遗传算法通过模拟自然选择过程来寻找最优解。以下是基于遗传算法的 TSP 实现: #### 遗传算法的核心操作 - **初始化种群**:随机生成若干条可能的路径作为初始种群。 - **适应度函数**:计算每条路径的距离总和,将其倒数作为适应度值。 - **选择**:根据适应度值挑选优秀的个体参与繁殖。 - **交叉**:交换两个父代的部分基因片段以生成子代。 - **变异**:随机改变某些城市的顺序以增加多样性。 ```python import random import numpy as np class GeneticAlgorithmTSP: def __init__(self, cities, population_size=100, mutation_rate=0.01, generations=500): self.cities = cities self.population_size = population_size self.mutation_rate = mutation_rate self.generations = generations def distance(self, city1, city2): return ((city1[0]-city2[0])**2 + (city1[1]-city2[1])**2)**0.5 def fitness(self, path): total_distance = sum([self.distance(path[i], path[i+1]) for i in range(len(path)-1)]) total_distance += self.distance(path[-1], path[0]) return 1 / total_distance def initialize_population(self): population = [] for _ in range(self.population_size): individual = list(range(len(self.cities))) random.shuffle(individual) population.append(individual) return population def crossover(self, parent1, parent2): start, end = sorted(random.sample(range(len(parent1)), 2)) child = [-1]*len(parent1) # Copy segment from first parent for i in range(start, end+1): child[i] = parent1[i] pointer = 0 for gene in parent2: if gene not in child and pointer < len(child): while child[pointer] != -1: pointer += 1 child[pointer] = gene return child def mutate(self, individual): for swapped in range(len(individual)): if random.random() < self.mutation_rate: swap_with = int(random.random() * len(individual)) individual[swapped], individual[swap_with] = individual[swap_with], individual[swapped] def evolve(self): population = self.initialize_population() best_path = None best_fitness = float('-inf') for generation in range(self.generations): new_population = [] for _ in range(int(self.population_size/2)): parents = random.choices(population, weights=[self.fitness(p) for p in population], k=2) offspring = self.crossover(parents[0], parents[1]) self.mutate(offspring) new_population.extend([parents[0], parents[1], offspring]) population = [p for _, p in sorted([(self.fitness(p), p) for p in new_population], reverse=True)[:self.population_size]] current_best = max(population, key=self.fitness) if self.fitness(current_best) > best_fitness: best_fitness = self.fitness(current_best) best_path = current_best return best_path, 1/best_fitness # Example usage cities = [(random.uniform(0, 100), random.uniform(0, 100)) for _ in range(20)] ga_tsp = GeneticAlgorithmTSP(cities=cities, population_size=100, mutation_rate=0.01, generations=500) best_route, shortest_distance = ga_tsp.evolve() print(f"Best Route: {best_route}") print(f"Shortest Distance: {shortest_distance:.2f} units") ``` --- ### 使用混合粒子群算法(GA-PSO)求解 TSP 混合粒子群算法结合了遗传算法和粒子群优化的优势,在探索能力和收敛速度上表现更优[^2]。 #### 混合算法的关键步骤 - 初始化粒子位置和速度向量。 - 计算每个粒子的位置对应于一条可行路径,并评估其适应度。 - 更新全局最佳解和个人历史最佳解。 - 利用遗传算法中的交叉和变异机制增强局部搜索能力。 ```python from pyswarm import pso def tsp_pso_objective_function(x, distances_matrix): n_cities = len(distances_matrix) tour = np.argsort(x).astype(int) cost = distances_matrix[tour[-1]][tour[0]] # Return to the starting point for i in range(n_cities-1): cost += distances_matrix[tour[i]][tour[i+1]] return cost n_cities = 20 distances_matrix = [[((i-j)**2+(k-l)**2)**0.5 for j,k in enumerate(np.random.rand(n_cities))] for i,l in enumerate(np.random.rand(n_cities))] lb = [0]*n_cities ub = [n_cities-1e-6]*n_cities optimal_x, optimal_cost = pso(tsp_pso_objective_function, lb, ub, args=(distances_matrix,), swarmsize=100, maxiter=500) print(f"Optimal Tour Order: {np.argsort(optimal_x)}") print(f"Minimum Cost: {optimal_cost:.2f} units") ``` --- ### 蚂蚁殖民地算法(ACA) 蚂蚁殖民地算法模仿自然界中蚂蚁觅食的行为模式,利用信息素更新机制逐步逼近最优解。 #### ACA 的主要流程 - 初始化参数如信息素浓度、启发因子权重等。 - 构建候选集并通过概率模型决定下一步移动的城市。 - 根据当前迭代的结果调整各边的信息素强度。 ```python import math class AntColonyOptimizer: def __init__(self, graph, num_ants=20, alpha=1, beta=5, evaporation_rate=0.5, iterations=100): self.graph = graph self.num_ants = num_ants self.alpha = alpha self.beta = beta self.evaporation_rate = evaporation_rate self.iterations = iterations self.pheromone = [[1 for _ in range(len(graph))] for _ in range(len(graph))] def select_next_city(self, ant_position, visited): pheromones = [self.pheromone[ant_position][j]**self.alpha * (1/self.graph[ant_position][j])**self.beta if j not in visited else 0 for j in range(len(self.graph))] probabilities = [p / sum(pheromones) for p in pheromones] next_city = np.random.choice(list(range(len(probabilities))), p=probabilities) return next_city def update_pheromone(self, paths): for i in range(len(self.graph)): for j in range(i+1, len(self.graph)): delta_tau = 0 for path in paths: if j in path[:-1]: index_j = path.index(j) if path[index_j-1] == i or path[index_j+1] == i: delta_tau += 1 / sum([self.graph[path[k]][path[k+1]] for k in range(len(path)-1)]) self.pheromone[i][j] *= (1-self.evaporation_rate) self.pheromone[j][i] = self.pheromone[i][j] += delta_tau def optimize(self): best_path = None min_cost = float('inf') for iteration in range(self.iterations): all_paths = [] for _ in range(self.num_ants): current_city = random.randint(0, len(self.graph)-1) visited = set([current_city]) path = [current_city] while len(visited) < len(self.graph): next_city = self.select_next_city(current_city, visited) path.append(next_city) visited.add(next_city) current_city = next_city cost = sum([self.graph[path[i]][path[i+1]] for i in range(len(path)-1)]) + self.graph[path[-1]][path[0]] if cost < min_cost: min_cost = cost best_path = path[:] all_paths.append(path[:]) self.update_pheromone(all_paths) return best_path, min_cost graph = [[0, 29, 20, 21], [29, 0, 15, 17], [20, 15, 0, 28], [21, 17, 28, 0]] aco = AntColonyOptimizer(graph=graph, num_ants=20, alpha=1, beta=5, evaporation_rate=0.5, iterations=100) best_path, min_cost = aco.optimize() print(f"Best Path Found by ACO: {best_path}") print(f"Total Minimum Cost: {min_cost:.2f} units") ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值