您的位置:首页 > 其它

最短路算法(小小总结一下)

2013-09-29 20:20 337 查看
1, Dijkstra算法

(1) 处理正边权可以处理负边权,但必须是负边只存在源点s连出去的边

(2) 时间复杂度……O(V^2)

例题:

In: 输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商 店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1& lt;=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。

(输入保证至少存在1条商店到赛场的路线。)

Out: 对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间

Code:

#include<iostream>

#include<stdio.h>

#include<iomanip>

using namespace std;

#define N 10000

#define MAX 100000099

int a

;


int dist
;


void input (int n,int m)

{

int p,q,len,i,j;

for(
i=1;i<=n;i++)


{


for(j=1;j<=n;j++)



a[i][j]=MAX;



dist[i]=MAX;


}


for(i=0;i<m;i++)


{


cin>>p>>q>>len;



if(len<a[p][q])


{


a[p][q]=len;



a[q][p]=len;


}

}

}

void dijkstra(int n)

{

int
s
,newdist;


for(int
i=1;i<=n;i++)


{


dist[i]=a[1][i];


s[i]=0;

}

dist[1]=0;

s[1]=1;


for(i=2;i<=n;i++)


{

int
j,tem=MAX;


int u=1;

for(j=2;j<=n;j++)


if(!s[j]&&dist[j]<tem)


{


u=j;



tem=dist[j];


}


s[u]=1;



for(j=2;j<=n;j++)


{


if(!s[j]&&a[u][j]<MAX)



{


newdist=dist[u]+a[u][j];


if(newdist<dist[j])



dist[j]=newdist;



}


}

}

}

int main()

{

int n,m;


while(scanf("%d%d",&n,&m),m||n)


{


input(n,m);


dijkstra(n);


cout<<dist
<<endl;


}

return 0;

}

2.Bellman-Ford算法

如果原图中不存在负环:

s可达的任意点v的最短路至多经过v1条边。对于这v1条边,第一次松弛后第一条边不可再松弛。第二次松弛后第二条边不可再松弛,…这样便证明了算法在没有负环的图上的正确性。

如果原图中存在负环:

显然对于负环中的任何一个点,它的最短路会是负无穷,所以松弛操作不会停止。算法返回false

时间复杂度: 两个for循环O(VE)

例题(hdu2544)

Code:

#include <stdio.h>

#define MAX 1000000000

int m, n, i, j ,k;

int start, end, value;

int dis[105], edge[105][105];

int Min (int a, int b)

{

return a
< b ? a : b;


}

void Bellman (int source)

{

for (i = 0;
i <= n; ++i)


{

dis[i] = edge[1][i];

}

for (k = 2;
k < n; ++k)


{

for (i =
1; i <= n; ++i)


{

for
(j = 1; j <= n; ++j)


{


dis[i] = Min(dis[i], dis[j] + edge[j][i]);


}

}

}

printf
("%d\n", dis
);


}

int main (void)

{

while (scanf
("%d%d", &n, &m) && (m || n))


{

for (i =
0; i <= n; ++i)


{

for
(j = 0; j <= n; ++j)


{


edge[i][j] = MAX;


}

}

for (i =
0; i < m; ++i)


{


scanf ("%d%d%d", &start, &end, &value);



edge[start][end] = edge[end][start] = value;


}

Bellman
(1);


}

return 0;

}

3.SPFA算法

一个Bellman-Ford算法优化:
每次只用距离减小的点去更新其他点

如何实现?队列!

思路:

Step 1:初始时所有点d[]值置INF,源点d[]0。将源点放进队列。

Step
2:
当队列不为空时每次从队列中取出队首,对队首的每条边进行松弛。将松弛后d[]值改变并且不在队列中的点加入队列

两点说明:

时间复杂度

最坏
: O(VE)
一般 : O(kE)

如何判负环?

对每个点记录一个num值,表示被更新了多少次,如果某个点被更新的次数超过n-1次,则有负环

例题(1544)

Code:

#include <stdio.h>

#define MAX 1000000000

struct Edge

{

int start;

int end;

int next;

int value;

} eg[20005];

int m, n, i, pot;

int startNode, endNode, value;

int first, tail, popNumber;

int dis[105], head[105], queue[30000];

bool chose[200];

void Build (int st, int nd, int val)

{

eg[pot].start
= st;


eg[pot].end
= nd;



eg[pot].value = val;


eg[pot].next
= head[st];


head[st] =
pot++;


}

void BFS (int x)

{

int k;

for (k =
head[x]; k != -1; k = eg[k].next)


{

if
(dis[eg[k].end] > dis[x] + eg[k].value)


{


dis[eg[k].end] = dis[x] + eg[k].value;


if
(chose[eg[k].end] == false)


{


queue[tail++] = eg[k].end;



chose[eg[k].end] = true;


}

}

}

}

void SPFA (void)

{

dis[1] = 0;

first = tail
= 0;



queue[tail++] = 1;


//printf
("first = %d\n", queue[first]);


chose[1] =
true;


while (tail
- first != 0)


{

//printf
("first = %d\n", queue[first]);



popNumber = queue[first];



chose[queue[first]] = false;


++first;

BFS
(popNumber);


}

printf
("%d\n", dis
);


}

int main (void)

{

while (scanf
("%d%d", &n, &m) && (m || n))


{

for (i =
0; i <= n; ++i)


{


head[i] = -1;


dis[i]
= MAX;



chose[i] = false;


}

pot = 0;

for (i =
0; i < m; ++i)


{


scanf ("%d%d%d", &startNode, &endNode, &value);



Build (startNode, endNode, value);



Build (endNode, startNode, value);


}

SPFA ();

}

return 0;

}

4.Floyd算法

时间复杂度: 三重for循环 O(V^3)

先看代码:

for(k=1;k<=V;k++)

for(i=1;i<=V;i++)

for(j=1;j<=V;j++)

if(dis[i][j]>dis[i][k]+dis[k][j])

dis[i][j]=dis[i][k]+dis[k][j]; 实际上是一个精巧的DP

DP过程:

dis[i][j][k]表示从ij的路径中,经过的点的编号不超过k的最短路

边界条件:dis[i][j][0] = dis[i][j]

转移方程:

dis[i][j][k] = Min(dis[i][j][k-1] , dis[i][k][k-1] +
dis[k][j][k-1])


(k > 0 , 0 <= i , j <= n)

答案:dis[i][j]


例题(1544)

Code:

#include <stdio.h>

int i, j, k, m, n, start, end, value;

int map[200][200], dis[200][200];

const int MAX = 1000000000;

int Min (int a, int b)

{

return a
< b ? a : b;


}

int main (void)

{

while
(scanf("%d%d", &n, &m) && (m || n))


{

for (i =
0; i <= n; ++i)


{

for
(j = 0; j <= n; ++j)


{


map[i][j] = MAX;


}

}

for (i =
0; i < m; ++i)


{


scanf ("%d%d%d", &start, &end, &value);



map[start][end] = map[end][start] = value;


}

for (k =
1; k <= n; ++k)


{

for
(i = 1; i <= n; ++i)


{


for (j = 1; j <= n; ++j)



{



map[i][j] = Min (map[i][j], map[i][k] + map[k][j]);



}


}

}

printf
("%d\n", map[1]
);


}

return 0;

}

<01>最短路算法的应用

求无向图最小环

最小环是指一个图中一个至少包含三个顶点的边权和最小的环

Floyd

设某环编号最大的顶点为
L
另两个顶点编号分别为 M
N (M,N < L)
,则最大编号为 L 的最小环长度即为mp(M,L)
+ mp(N,L) + dp(M,N)
,其中 dp(M,N) 表示以
0
L-1 号顶点为中间点时的最短路径,刚好符合
Floyd
算法最外层循环到 k=L 时的情况。

<02>若为有向图呢?(CDOJ
1329)


最短路算法的应用

求下面不等式组的一组解:

X1 - X2 <= 0 X1 - X5 <= -1 X2 - X5 <= 1 X3 -
X1 <= 5 X4 - X1 <= 4 X4 - X3 <= -1 X5 - X3 <= -3 X5 - X4 <= -3


Xi<=0(i=1,2,3,4,5)

求完最短路后图中每条边

dis(v) <= dis(u) + w(u, v)

对于不等式Xi - Xj <= c,把它化成不等式:Xi
<= Xj + c
,就可以化成边Vj -> Vi,权值为c。最后,我们在这张图上求一次单源最短路径,这些不等式就会全部都满足

<03>最短路算法的应用

求下面不等式组的一组解:

X1 - X2 <= 0 X1 - X5 <= -1 X2 - X5 <= 1 X3 -
X1 <= 5 X4 - X1 <= 4 X4 - X3 <= -1 X5 - X3 <= -3 X5 - X4 <= -3


Xi<=0(i=1,2,3,4,5)

对于最后一个Xi<=0怎么办?

新添一个X0=0,并使Xi-X0<=0!

最后以X0为源点,跑最短路

最后每个点的dis值就为答案

注意:由于有负边权,不能使用dijkstra
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: