您的位置:首页 > 其它

最短路的几种算法及其优化(模板)

2016-04-25 14:19 429 查看
一.Dijkstra 算法

dijkstra算法适用于边权为正的情况,求单源最短路,适用于有向图和无向图

模板伪代码:

清除所有点的标号

设d[0]=0,其余d[i]=INF;

循环n次{

在所有未标记的节点中,寻找d[i]最小的点x

给x做标记

对于从x出发的所有边(x,y)更新d[y]=min(d[y],d[x]+w[x,y]);

}

memset(v,0,sizeof(v));
for(int i=0;i<n;++i)
d[i]=(i==0?0:INF);
for(int i=0;i<n;++i)
{
int x,m=INF;
for(int j=0;j<n;++j)
if(!visit[j]&&d[j]<m)
{
m=d[j];
x=j;
}
visit[x]=1;
for(int j=0;j<n;++j)
d[j]=min(d[j],d[x]+w[x][j]);
}


简单说一下dijkstra的优化:

1.储存结构上:邻接矩阵是很占空间的(众所周知),所以我们一般采用邻接表或者边表

2.堆优化:因为在dijkstra中有循环n次寻找最小dict的过程,我们可以维护一个小根堆来实现,也就把时间复杂度从n^2降到了n*(logn+E)。

优化后的dijkstra,自己写的:

/*
建图用的邻接表,复杂度O(E*logE)
*/

struct pnode {
int num;
int len;

pnode() {}
pnode(int a, int b) : num(a), len(b) {}//初始化结构体用的,把a复制给num,把b复制给len;
bool operator < (const pnode tmp) const {
return len > tmp.len;
}
};

int dis
;
bool vis
;
int n;

void dijkstra(int s) {
priority_queue<pnode> q;
q.push(pnode(s, 0));
pnode u;
int v, i, res = inf;
for(i = 0; i <= n; ++i) dis[i] = inf, vis[i] = false;
dis[s] = 0;

while(!q.empty()) {
u = q.top(); q.pop();
if(u.len != dis[u.num]) continue;/*这是应对优先队列中的重复入队的点,只要最新的那个点就可以了*/
if(vis[u.num])  continue;
vis[u.num] = true;

for(i = head[u.num]; i != -1; i = g[i].next) {
v = g[i].to;
if(dis[v] > u.len + g[i].val) {
dis[v] = u.len + g[i].val;
q.push(pnode(v, dis[v]));
}
}
}
}


二.Bellman-Ford的优化(也就是SPFA,直接看三吧)

三.SPFA模板及SPFA的优化

1.普通SPFA模板(队列化的Bellman-Ford算法):

int visit
,dis
;
bool SPFA(int s)
{
queue<int>q;
memset(dis,127,sizeof(dis));
memset(visit,false,sizeof(visit));
memset(cnt,0s,sizeof(cnt));
dis[s]=0;
visit[s]=true;
q.push(s);
while(!q.empty())
{
int k=q.front();
q.pop();
visit[k]=false;
for(int i=head[k];i;i=edge[i].last)/*边表*/
{
if(dis[k]+edge[i].w<dis[edge[i].v])
{
dis[edge[i].v]=dis[k]+edge[i].w;
if(!visit[edge[i].v])
{
q.push(edge[i].v);
visit[edge[i].v]=true;
if(++cnt[edge[i].v]>n) /*如果某一个点的入队次数超过了n次,说明存在负环,返回false*/
return false;
}
}
}
}
return true;/*安全跑完了,不存在环*/
}


2.SPFA的优化

SPFA算法有两个优化策略SLF和LLL——SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾; LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出队进行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。实际上dijkstra算法+heap优化后是一定快于一般SPFA的,而且更加稳定。

1)SPFA的SLF优化,(很简单的,只要使用双端队列就可以实现了)。

#include<iostream>
using namespace std;
#include<deque>
#include<cstdio>
#include<cstring>
int n,m;
#define N 1001
struct Edge{
int u,v,w,last;
}edge
;
int head
;
int dict
;
bool visit
;
void input()
{
scanf("%d%d",&n,&m);/*n个点,m条边*/
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
edge[i].last=head[edge[i].u];
head[edge[i].u]=i;
}
}
bool SPFA()
{
deque<int>q;
memset(dict,127,sizeof(dict));
memset(visit,false,sizeof(visit));
dict[1]=0;
visit[1]=true;;
int cnt
;
memset(cnt,0,sizeof(cnt));/*判断有无环的标志*/
++cnt[1];
while(!q.empty())
{
int k=q.front();
q.pop_front();
visit[k]=false;/*取出后,不要忘记标志位*/
for(int l=head[k];l;l=edge[l].last)/*边表*/
{
int p=edge[l].v;
if(dict[p]>dict[k]+edge[l].w)
{
dict[p]=dict[k]+edge[l].w;
++cnt[p];
if(cnt[p]>n) return true;/*如果某个点的入队次数超过了n,那么一定存在环*/
if(!visit[p])
{
visit[p]=true;
if(!q.empty())/*这就是SLF Small Label First 策略.的核心,把将要入队的元素的dict与队首元素相比较,如果将要入队的元素的dict大的话,就放在队尾,否则就放在队首 */
{
if(dict[p]>dict[q.front()])/*这样可以保证始终用dict小的更新,也节约了时间*/
q.push_back(p);
else q.push_front(p);/*所以必须使用双端队列*/
}
else q.push_back(p);/*不要忘记考虑队列为空的情况*/
}
}
}
}
return false;
}
int main()
{
input();
if(SPFA())
printf("circle");
else{
for(int i=1;i<=n;++i)
printf("%d ",dict[i]);
}
return 0;
}


自己打了一遍,还可以理解
2.SPFA的LLL优化

在网上实在没找到可靠的。

3.SPFA的DFS优化:

在很多的题目中,SPFA都是用BFS来实现的,对于随机图来说,BFS的速度会远大于DFS,但是对于某些特殊的图结构来说,DFS也是一个很好的选择

例如 1):题目中要求在最短的时间内,判断有无环,DFS明显会比BFS快(例题是POj上一个判断单词接龙的题目)

2):对于网格型的图,DFS的速度比BFS快

模板:

void SPFA(int k)
{
flag[k]=true;
for(int l=head[k];l;l=edge[l].last)
{
int v=edge[l].v;/*找出第一个可以被更新的点*/
if(dis[v]>dis[k]+edge[l].w)
{
dis[v]=dis[k]+edge[l].w;
if(!flag[v])
{
SPFA(v);/*接着深搜下去*/
}
else /*这表明从某个点开始DFS,结果又搜到了这个点,说明存在负权回路*/
{
printf("cycle");
return;
}
}
}
flag[k]=false;/*不要忘记再把k设为false,为的是让他能够重复入队*/
}


四,Floyd算法模板(没有任何优化,就是邻接矩阵和n^3,一般情况单源最短路是绝对不会用的)

for(int k=1;k<=n;++k)/*注意这个k必须在最外层循环才行*/

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

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

dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: