单源最短路 Dijkstra算法 和 SPFA算法
單源最短路
?從一個點出發,到達其他頂點的最短路徑的長度。
?基本操作:松弛
?d[u]+map[u, v]< d[v]這樣的邊(u,v)稱為緊的(tense),可以對它進行松弛(relax):
?d[v] = d[u]+w, pred[v] = u
?最開始給每一個點一個很大的值,dis[v]=inf,從d[s]=0開始,不斷的對可以松弛的點進行松弛,不能松弛的時候就已經求出了最短路
Dijkstra算法
?Dijkstra(迪杰斯特拉)算法是典型的單源最短路徑算法,用于計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴展,直到擴展到終點為止
?可以證明,具有最小的d[i](臨時最短路)值的(還沒加入最短路)點在此以后無法松弛
?所以每次找最近的點進行松弛操作
注意該算法要求圖中不存在負權邊
?Dijkstra算法也適用于無向圖。但不適用于有負權邊的圖。
?d[1,2] = 2 ,但用Dijkstra算法求得 d[1,2] = 3
算法步驟:
1、在開始之前,認為所有的點都沒有進行過計算,dis[i]全部賦值為極大值(dis[i]表示各點當前到源點的最短距離)
2、源點的 dis[start] 值明顯為0
3、計算與 start 相鄰的所有點的dis值 —— dis[v] = map[s][v]
4、還沒算出最短路的點中dis[]最小的一個點u, 其最短路就是當前的dis[u]
5、對于與u相連的所有點v,若dis[u]+map[u][v] 比當前的dis[v]小, 更新dis[v]
6、重復4,5直到源點到所有點的最短路都已求出
const int inf = 0x3f3f3f3f; //需將road及dis初始化為正無窮inf int n,m; int dis[maxn]; //儲存各個點到源點的最短距離,dis[s]為0 int road[maxn][maxn]; //兩點之間直接距離關系 bool vis[maxn]; //判斷源點到該點的距離是否為最短距離 void dijkstra(int s) {memset(vis, false, sizeof(vis));//標記是否求出最短路徑for(int i = 1; i <= n; i++)dis[i] = road[s][i];//初始化起點到每一個點的距離vis[s] = true;//標記起點到這一點的最小距離已經求出dis[s]=0;for(int u = 1; u<n; u++){int minD = inf,k = -1;for(int i = 1; i<= n; i++){if(!vis[i]&&dis[i]<minD){k = i;//記錄下標minD = dis[i];//記錄最小值}}vis[k] = true;//標記已經訪問過//松弛操作for(int i = 1; i<= n; i++){if(!vis[i]&&dis[k]+road[k][i]<dis[i]){dis[i]=dis[k]+road[k][i];}//if}//for} }堆優化的Dijkstra算法,使用優先隊列來實現小根堆
#include<cstdio> #include<queue> #include<algorithm> #include<cstring> using namespace std; #define inf 0x3f3f3f3f const int maxn=1007; int dis[maxn]; bool vis[maxn]; int m,n; struct qnode{int v,c;qnode(int _v=0,int _c=0):v(_c),c(_c){}bool operator<(const qnode&r)const {return c>r.c;}//小根堆 };struct Edge{int v;int cost;Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}//構造方法 };vector<Edge> E[maxn]; void dijkstra(int start) {memset(vis,0,sizeof (vis));memset(dis,inf,sizeof (dis));priority_queue<qnode> que;qnode s;s.c=0;s.v=start;que.push(s);dis[start]=0;//起點的最短距離為0while(!que.empty()){qnode tmp=que.top();que.pop();int u=tmp.v;if(vis[u]) continue;vis[u]=true;for(int i=0;i<E[u].size();i++){int v=E[u][i].v;int cost=E[u][i].cost;if(!vis[v]&&dis[v]>dis[u]+cost){dis[v]=dis[u]+cost;qnode next;next.v=v;next.c=dis[v];que.push(next);}}} } //建邊 void addedge(int u,int v,int w) {E[u].push_back(Edge(v,w));E[v].push_back(Edge(u,w)); }int main() {while(scanf("%d%d",&m,&n)!=EOF){// printf("%d %d\n",m,n);for(int i=0;i<m;i++){int u,v,w;scanf("%d%d%d",&u,&v,&w);addedge(u,v,w);}dijkstra(n);printf("%d\n",dis[1]);} }SPFA算法
?Dijkstra算法每一次松弛的時候bellmanford都要枚舉所有的點,而其實有很多點都是不需要枚舉的,所以有很多的無效枚舉,于是效率顯得略低
?SPFA算法(Shortest Path Faster Algorithm)每次松弛的時候只需要枚舉與上次被松弛的點相連的點就可以了
SPFA算法的實現
先將源點入隊,然后重復從隊首取出一個頂點,對所有該點指向的頂點進行松弛,如果松弛成功并且該不在隊列中,直到隊空,即可找出源點到所有頂點的最短距離。時間復雜度為O(KM)。M為邊數,k是每個點平均入隊次數。?
#define N 105 #define inf 0x3f3f3f3f int res[N];//存儲源點到頂點的最短距離的值 int g[N][N];//存儲兩點之間的直接距離關系 int cnt[N];//每個點入隊的次數,判斷是否出現負環 int que[N*N];//隊列 bool in_que[N];//標記一個點是否在隊列中 int front;//隊首位置 int rear;//隊尾位置 void spfa(int n,int start) {memset(res,inf,sizeof(res));memset(in_que,0,sizeof(in_que));rear=front=0;que[++rear]=start;//起點入隊in_que[start]=true;//起點在隊列中res[start]=0;while(front<rear){int cur=que[++front];in_que[cur]=0;//出隊int i;for(i=1;i<=n;i++){if(res[cur]+g[cur][i]<res[i]){res[i]=res[cur]+g[cur][i];if(!in_que[i]){que[++rear]=i;in_que[i]=true;//入隊}}}} }STL 隊列,實現簡單,浪費時間
bool vis[maxn];//標記點是否在隊列 double mp[maxn][maxn]; double dis[maxn];void spfa(int from,int to) {memset(vis,0,sizeof vis);//memset(dis.inf,sizeof dis);for(int i=0;i<maxn;i++)dis[i]=inf;queue<int> q;q.push(from);vis[from]=true;dis[from]=0;while(!q.empty()){int cur=q.front();q.pop();vis[cur]=false;for(int i=0;i<n;i++){if(dis[i]>dis[cur]+mp[cur][i]){dis[i]=dis[cur]+mp[cur][i];if(!vis[i]){vis[i]=true;q.push(i);}}}} }使用堆棧實現的SPFA,比隊列要快(POJ1847,學好英語)
#include<stdio.h> #include<iostream> #include<string.h> #include<queue> #include<cstdio> #include<string> #include<math.h> #include<algorithm> #include<map> #include<set> #include<stack> //#define mod 998244353 #define INF 0x3f3f3f3f #define eps 1e-6 using namespace std; typedef long long ll; const int mod=1e9+7; const int maxn=1000; int n; int cnt; int head[maxn]; struct Edge {int to;int next;int val; }edge[maxn]; //建邊:from->to,邊權為val void addEdge(int from,int to,int val) {edge[cnt].to=to;edge[cnt].val=val;edge[cnt].next=head[from];head[from]=cnt++; }bool inq[maxn];//標記是否在隊列中 int dis[maxn];//到起點的最短距離 int q[maxn];//隊列void spfa(int start) {memset(inq,0,sizeof inq);memset(dis,INF,sizeof dis);int top=0;q[++top]=start;//queue<int>q;//q.push(start);inq[start]=true;dis[start]=0;while(top){//int cur=q.front();//q.pop();int cur=q[top--];inq[cur]=false;for(int i=head[cur];i!=-1;i=edge[i].next){int to=edge[i].to;if(dis[to]>dis[cur]+edge[i].val){dis[to]=dis[cur]+edge[i].val;if(!inq[to])inq[to]=1,q[++top]=to;//入隊}}} } int main() {int s,t;while(scanf("%d%d%d",&n,&s,&t)!=EOF){memset(head,-1,sizeof head);cnt=0;for(int i=1;i<=n;i++){int sum;scanf("%d",&sum);for(int j=0;j<sum;j++){int x;scanf("%d",&x);if(j==0)addEdge(i,x,0);elseaddEdge(i,x,1);}}spfa(s);if(dis[t]==INF)printf("-1\n");elseprintf("%d\n",dis[t]);}return 0; }?
總結
以上是生活随笔為你收集整理的单源最短路 Dijkstra算法 和 SPFA算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dp进阶之FFT加速+数据结构优化+不等
- 下一篇: HDU1878 欧拉回路