图论算法----最短路

经典算法

单源最短路:

1.Bellman_ford(可判负环,可有负边)

d[i]表示起点S到i的最短路,那么d[i]=min{d[j]+w[j][i]}且存在j->i的边代价为w[j][i]

经过证明如果不存在负圈最多通过V-1次松弛就可以完成复杂度O(V*E)(V为结点数,E为边数)

 1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <set>
5 #include <algorithm>
6 #include <map>
7 #include <queue>
8 #include<vector>
9 #define maxn 50010
10 #define maxm 100010
11 #define INF 0x3fffffff
12 using namespace std;
13 struct edge{
14 int from,to,w;
15 edge(){}
16 edge(int _u,int _v,int _w){
17 from=_u,to=_v,w=_w;
18 }
19 };
20 edge G[maxm];
21 int V,E;
22 void addedge(int u,int v,int w){
23 G[E++]=edge(u,v,w);
24 }
25 int d[maxn];
26 int n,m;
27 //d[i] = min{d[j]+w[j][i]} {j->i}
28 void bellman_ford(int s){
29 V=n;
30 for(int i=0;i<V;++i)d[i]=INF;
31 d[s]=0;
32 while(1){
33 bool flag =false;
34 for(int i=0;i<E;++i){
35 edge e = G[i];
36 if(d[e.from]!=INF&&d[e.to]>d[e.from]+e.w){
37 d[e.to] = d[e.from]+e.w;
38 flag = true;
39 }
40 }
41 if(!flag)break;
42 }
43 }
44 int main (){
45 while(scanf("%d%d",&n,&m)!=EOF){
46 if(n==0&&m==0)break;
47 int u,v,w;
48 E=0;
49 for(int i=0;i<m;++i){
50 scanf("%d%d%d",&u,&v,&w);
51 addedge(u-1,v-1,w);
52 addedge(v-1,u-1,w);
53 }
54 bellman_ford(0);
55 printf("%d\n",d[n-1]);
56 }
57 }

判负环:看一下是不是多于V-1次松弛,如果有则存在负环

 1 bool HaveNagativeLoop(){
2 memset(d,0,sizeof(d));
3 for(int i=0;i<V;++i){
4 for(int j=0;j<E;++j){
5 edge e = G[j];
6 if(d[e.to]>d[e.from]+e.w){
7 d[e.to] = d[e.from]+e.w;
8 if(i==V-1)return true;
9 }
10 }
11 }
12 return false;
13 }

2.dijkstra(无负边)

对于上述的d[i]=min{d[j]+w[j][i]}如果d[j]当前值不是d[j]能达到的最小值那么显然在后面d[i]还将更新,如何避免这种情况

选择d[j]已经是最小,即S->j的最短距离不再更新,如何选取?每次选择距离最短且未被使用的结点,用这个结点对其他节点进行松弛复杂度O(V*V)

 1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <set>
5 #include <algorithm>
6 #include <map>
7 #include <queue>
8 #include<vector>
9 #define maxn 1010
10 #define maxm 100010
11 #define INF 0x3fffffff
12 using namespace std;
13 //d[i]=min{d[j]+w[j][i]} {d[j]已经不再更新}
14 //每次找最小d[j]然后松弛
15 int G[maxn][maxn];
16 int d[maxn];
17 bool used[maxn];
18 int V;
19 void dijksta(int s){
20 for(int i=0;i<V;++i){
21 used[i]=false;
22 d[i]=INF;
23 }
24 d[s]=0;
25 while(1){
26 int v =-1;
27 for(int i=0;i<V;++i){
28 if(!used[i]&&(v==-1||d[i]<d[v]))v=i;
29 }
30 if(v==-1)break;
31 used[v]=true;
32 for(int i=0;i<V;++i){
33 d[i]=min(d[i],d[v]+G[v][i]);
34 }
35 }
36 }
37 void init(int n){
38 V = n;
39 for(int i=0;i<V;++i){
40 for(int j=0;j<V;++j){
41 if(i==j)G[i][j]=0;
42 else G[i][j]=INF;
43 }
44 }
45 }
46 int main (){
47 int n,m;
48 while(scanf("%d%d",&n,&m)!=EOF){
49 if(n==0&&m==0)break;
50 int u,v,w;
51 init(n);
52 for(int i=0;i<m;++i){
53 scanf("%d%d%d",&u,&v,&w);
54 G[u-1][v-1]=w;
55 G[v-1][u-1]=w;
56 }
57 dijksta(0);
58 printf("%d\n",d[n-1]);
59 }
60 }

来优化一下上面的算法,每次需要选择最短的一个结点,这个可以使用一个优先队列来维护当前所有边里面权值最小的边,所以算法复杂度变为O(V*log(E))

使用邻接表存储图比较容易写

 1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <set>
5 #include <algorithm>
6 #include <map>
7 #include <queue>
8 #include<vector>
9 #define maxn 1010
10 #define maxm 100010
11 #define INF 0x3fffffff
12 using namespace std;
13 struct edge {
14 int to,w;
15 edge(){}
16 edge(int _to,int _w){
17 to=_to;w=_w;
18 }
19 };
20 typedef pair<int,int> P;
21 int V;
22 vector<edge> G[maxn];
23 int d[maxn];
24 void dijkstra(int s){
25 priority_queue<P,vector<P>,greater<P> >q;
26 for(int i=0;i<V;++i)d[i]=INF;
27 d[s]=0;
28 q.push(P(d[s],s));
29 while(!q.empty()){
30 P p = q.top();
31 q.pop();
32 int v = p.second;
33 if(d[v]<p.first)continue;
34 for(int i=0;i<G[v].size();++i){
35 edge e = G[v][i];
36 if(d[e.to]>d[v]+e.w){
37 d[e.to]=d[v]+e.w;
38 q.push(P(d[e.to],e.to));
39 }
40 }
41 }
42 }
43 int main (){
44 int n,m;
45 while(scanf("%d%d",&n,&m)!=EOF){
46 for(int i=0;i<n;++i)G[i].clear();
47 V=n;
48 if(n==0&&m==0)break;
49 int u,v,w;
50 for(int i=0;i<m;++i){
51 scanf("%d%d%d",&u,&v,&w);
52 G[u-1].push_back(edge(v-1,w));
53 G[v-1].push_back(edge(u-1,w));
54 }
55 dijkstra(0);
56 printf("%d\n",d[n-1]);
57 }
58 }

3.floyd(任意两点的最短路,可有负边)

dp的思想   d[k+1][i][j]表示从0~k 里面选择中间结点从i到j的最短距离
那么当k为-1时d[0][i][j]=w[i][j]

考虑如何转移 从0~k-1中间选到0~k中选新的方案有两种情况:

不经过第k个结点 那么 d[k+1][i][j]=d[k][i][j]

经过第k个结点   那么d[k+1][i][j] = d[k][i][k]+d[k][k][j]

则方程为d[k+1][i][j]=min{ d[k][i][j],d[k][i][k]+d[k][k][j]}

可以使用滚动数组来优化空间,由于d[k+1][][]只与d[k][][]有关,所以只要保证k是从小到大枚举的就可以节省以为空间,复杂度为O(V*V*V)

 1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <set>
5 #include <algorithm>
6 #include <map>
7 #include <queue>
8 #include<vector>
9 #define maxn 1010
10 #define maxm 100010
11 #define INF 0x3fffffff
12 using namespace std;
13 int d[maxn][maxn];
14 int V;
15 void init(int n){
16 V=n;
17 for(int i=0;i<V;++i){
18 for(int j=0;j<V;++j){
19 if(i==j)d[i][j]=0;
20 else d[i][j]=INF;
21 }
22 }
23 }
24 void floyd(){
25 for(int k=0;k<V;++k){
26 for(int i=0;i<V;++i){
27 for(int j=0;j<V;++j){
28 d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
29 }
30 }
31 }
32 }
33 int main (){
34 int n,m;
35 while(scanf("%d%d",&n,&m)!=EOF){
36 if(n==0&&m==0)break;
37 int u,v,w;
38 init(n);
39 for(int i=0;i<m;++i){
40 scanf("%d%d%d",&u,&v,&w);
41 d[u-1][v-1]=w;
42 d[v-1][u-1]=w;
43 }
44 floyd();
45 printf("%d\n",d[0][n-1]);
46 }
47 }

时间: 2024-10-10 18:04:59

图论算法----最短路的相关文章

图论算法(5) --- 双向广搜求最短路(Bidirectional Breadth First Search)

我们知道,在图论算法中,求最短路是最基本的问题.在求最短路的问题中,应用双向广度优先搜索算法,又是一个较为高效而又简单的算法.所谓双向广度优先搜索,其实根本的核心还是BFS,只不过它是从起点和终点两头同时搜索,大大提高了搜索效率,又节省了搜索空间.广搜大家知道当然是用队列来实现了,在这里,要注意的问题就是,我们必须按层搜索,正向队列处理一层,接着去处理反向队列的一层,按层交替进行,而不是按节点交替进行,这点需要注意,其他的也就很简单了,代码中附有注释,如有问题请留言. package simil

图论算法之最短路径

图论算法之最短路径 作者:jasonkent27 转载请注明出处:www.cnblogs.com/jasonkent27 1. 前言 1.1 最短路引入 小明和小天现在住在海口(C1),他们俩计划暑假到三亚(C4)玩一趟,在海口和三亚之间有许多中间城市(文昌,临高,乐东,万宁...)图中的边上的数字是他们到达该城市必须的花费,现在需要你帮他们俩找出一条从海口到三亚的最省钱的路径出来. 等等,图中的边的weight怎么会有负的呢?你暂且可以这么理解吧.图中的边上的weight可以当作他们旅途中必须

基础图论算法导引

ACM中常用图论算法 1. 拓扑排序 -> 拓扑排序的原理及其实现 2. 最短路径算法 -> 最短路算法总结 差分约束系统 -> 差分约束 前k短路 -> 前K短路径问题 3. 最小生成树问题扩展 -> 最?小?生?成?树?问?题?的?拓?展  最优比率生成树 -> 最优比率生成树 最小k度限制生成树 -> IOI2004国家集训队论文,由汪汀所著(网盘内有) 或者刘汝佳的黑书内有 裸题 poj1639 题解 4. 二分图匹配 -> 二分图的最大匹配.完美匹

图论算法的数学模型

目录 图论算法的数学模型 引入:最短路的数学形式 最小割的数学形式 一些没用的总结 图论算法的数学模型 今天听敦敦敦的课总结一下... 前置芝士:网络流,最小割 引入:最短路的数学形式 松弛操作: 对于一条边\((u,v,w)\),\(\text {if}~(dis_u+w(u,v)<dis_v)~\text{then}~dis_v=dis_u+w(u,v)\) 所以对于求出来的dis,有\(dis_v\leq dis_u+w(u,v)\)对吧... 那么这和差分约束中\(x_i-x_j\leq

图论算法----强连通

poj 2186 Popular Cows 分析:直接求一下强连通分量,对于同一个强连通分量里面的结点状态是相同的,要求有多少个人被其他所有的人都认可,只有可能是拓扑排序的最后一个强连通的结点个数,判断一下其他节点是否都可以到该联通分量就ok了. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <set> 5 #include <algorithm>

图论之最短路03——任意指定点到所有其它点的最短路径及距离

======================================================== 重要程度 ***** 求任意点到其它点间最短距离及其路径. 输入:权值矩阵,起点 输出如下: 点i->点j 路径  ¥¥¥ 距离  ¥¥¥ 说明:必须调用E:\matlab M文件\liangdianzuiduanlu.m,请查看网址: http://blog.csdn.net/lzx19901012/article/details/47832213<图论之最 短路02-1--任意

图论算法-Tarjan模板 【缩点;割顶;双连通分量】

图论算法-Tarjan模板 [缩点:割顶:双连通分量] 为小伙伴们总结的Tarjan三大算法 Tarjan缩点(求强连通分量) int n; int low[100010],dfn[100010]; bool ins[100010]; int col[100010];//记录每个点所属强连通分量(即染色) vector<int> map[100010]; stack<int> st; int tot;//时间戳 int colnum;//记录强连通分量个数 void tarjan(

再谈排序与图论算法

排序 1.主存能放下的数据进行排序称为内部排序,反之称为外部排序(磁盘上).2.任何进行交换相邻元素进行排序的算法均需要O(N2)的复杂度,任何进行比较的排序算法至少需要O(N*log(N))的算法复杂度. 3.堆排序和归并排序的时间复杂度平均和最坏均为O(N*log(N)) 4.Java中执行一次对象比较是比较昂贵的,移动则是相对节省的,因此归并排序是java的默认泛型排序算法.C++中默认的是快速排序,比较耗费小:快排对于基本类型均具有最快速度.快速排序选取枢纽元的时候采用三数取中,切勿采用

【WIP_S9】图论算法

创建: 2018/06/01 图的概念 有向边 有向图 无向边 无向图 点的次数: 点连接的边的数量 闭路: 起点和重点一样 连接图: 任意两点之间都可到达 无闭路有向图: 没有闭路的有向图 森林: 互素的树的集合 生成树: 含有图里所有点的树 生成树林: 所有生成树的并集         图论算法的应用     ● 电路的元件关系 ● 交通网 ● 电脑网络(本地网络, 互联网, web等) ● 数据库(实体关系图(ER图))