这里给了最短路问题中三大算法及其优化后的算法总结和模板,总结一下,以便后续学习。node
多源最短路,即要求求出图中每两个顶点之间的最短路。虽然Floyed的复杂度是$O(n^3)$,可是4行却简单不少,本质上是动态规划算法。 思想:从i号顶点到j号顶点只通过前k号顶点的最短路径。算法
const int inf=0x3f3f3f3f; int Floyd() {//初始化n个顶点 for(i = 1; i <= n; i ++) for(j = 1; j <= n; j ++) if(i == j) e[i][j] = INF; else e[i][j] = 0; for(k = 1; k <= n; k ++)//Floyd-Warshall算法核心语句 for(i = 1; i <= n; i ++) for(j = 1; j <= n; j ++) if(e[i][j] > e[i][k]+e[k][j]) e[i][j] = e[i][k]+e[k][j]; }
Bellman-Ford算法能解决存在负权边的单源点最短路径问题。能够检测一个图是否存在负权回路:若是在进行了n-1轮松弛后,仍然能够继续成功松弛,说明存在负权边。数组
const int inf=0x3f3f3f3f; struct node{ int from,to,w; }; node edge[100]; bool Bellman(int s) { for(i = 1; i <= n; i ++) dis[i] = inf; dis[s]=0; bool flag; for(i = 1; i <= n; i ++) { flag = false; for(j = 1; j <= m; j ++) { x = edge[j].from ; y = edge[j].to ; z = edge[j].w ; if(dis[y] > dis[x] + z) { dis[y] = dis[x] + z; flag = true; } } if(!flag) //最好加上这句话,很重要 break; //若是更新到n遍,还可以继续更新dis数组,说明存在负权环 if(flag&&i == n) return false;//返回false表示这个图存在负权环 } return true;//返回true表示求最短路成功 }
每次实施一次松弛操做后,就会有一些顶点已经求得其最短路,此后这些顶点的最短路的估计值就会一直保持不变,再也不受到后面松弛的影响,可是每次还要判断是否须要松弛,浪费了时间。学习
因此这里每次仅仅对最短路的估计值发生了变化了的顶点的全部出边执行松弛操做优化
//poj 3169 Layout 题解 const int inf=0x3f3f3f3f; const int maxn=1e3+7; struct edge { int to, cost; }; int dis[maxn]; bool inq[maxn]; //判读一个点是否在队列中 int cnt[maxn]; vector<edge> g[maxn]; queue<int> que; int n, ml, md; void init() { for(int i=1; i<=n; i++) { g[i].clear(); inq[i]=false; dis[i]=inf; cnt[maxn]=0; } while(!que.empty()) que.pop(); } bool spfa(int s, int n) { edge e; inq[s]=true; dis[s]=0; que.push(s); while(!que.empty()) { int u=que.front(); que.pop(); inq[u]=false; //出队,不在队列中,赋值为false; for(int i=0; i<g[u].size(); i++) { e=g[u][i]; if(dis[e.to] > dis[u]+e.cost) { dis[e.to]=dis[u]+e.cost; if(!inq[e.to]) //若是以前没有在队列中,那么就能够入队了。 { inq[e.to]=true; que.push(e.to); cnt[e.to]++; if(cnt[e.to]>=n) return false;//说明有负环 } } } } return true;//没有负环,而且完成最短路 }
Dijkstra算法适合不含负权边的单源最短路(单源最短路是指从源点到其他各个顶点的最短路径)。 思想:每次找到离源点最近的一个顶点,而后以该顶点为中心进行拓展,最终获得源点到其他全部点的最短路径spa
void dij(int s) { for(int i=1; i<=n; i++) dis[i]=road[s][i]; vis[s]=1; dis[s]=0; //这句话也能够不加 for(int i=1; i<n; i++) { int tmp=inf, k; for(int j=1; j<=n; j++) { if(!vis[j] && dis[j] < tmp) { tmp=dis[j]; k=j; } } if(tmp==inf) //若是没有更新成功,那么接下来也不会再次更新,能够结束循环了。 break;//这句话之后就必须加上了。 vis[k]=1; for(int j=1; j<=n; j++) { if(!vis[j] && dis[j] > dis[k]+road[k][j]) dis[j]=dis[k]+road[k][j]; } } }
//邻接表+优先队列优化 题目poj 2387 til the cows come home const int maxn=1e3+7; const int inf=0x3f3f3f3f; struct edge{ int to, cost; }; struct node{ int d, u; bool friend operator < (const node a, const node b) { return a.d > b.d; } }; int dis[maxn]; int vis[maxn]; vector<edge> g[maxn]; priority_queue<node> que; int t, n; void init() { for(int i=1; i<=n; i++) { g[i].clear(); vis[i]=0; dis[i]=inf; } while(!que.empty()) que.pop(); } void dij(int s) { int u; edge e; dis[s]=0; node tmp={0, s}, next; que.push(tmp); while(!que.empty()) { tmp=que.top(); que.pop(); u=tmp.u; if(vis[u]==1) continue; vis[u]=1; for(int i=0; i<g[u].size(); i++) { e=g[u][i]; if(!vis[e.to] && dis[e.to] > dis[u]+e.cost)//注意要加判断是否访问 { dis[e.to]=dis[u]+e.cost; next.d=dis[e.to]; next.u=e.to; que.push(next); } } } }