用HDU2544整理一下最近学的最短路算法
1.Dijkstra算法
原理:集合S表示已经找到最短路径的点,d[]表示当前各点到源点的距离
初始时,集合里面只有源点,当每个点u进入集合S时,用d[u]+w[u][v]更新距离
再重复这个步骤,选取S外所有点中d[]最小的进入集合
直到所有的点都进入S集合
局限性:图的边权必须为正
复杂度:O(V*V),堆优化((E+V)logV)
(1)用邻接矩阵实现
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7 #include<map> 8 #include<set> 9 #include<queue> 10 #include<algorithm> 11 using namespace std; 12 13 typedef long long LL; 14 const int INF = (1<<30)-1; 15 const int mod=1000000007; 16 const int maxn=10005; 17 int w[maxn][maxn],d[maxn],used[maxn]; 18 int n,m; 19 20 void dijkstra(int s){ 21 memset(used,0,sizeof(used)); 22 for(int i=1;i<=n;i++) d[i]=INF; 23 d[s]=0; 24 25 for(int k=1;k<=n;k++){ 26 int p,m=INF; 27 for(int i=1;i<=n;i++) if(!used[i]&&d[i]<m) m=d[p=i]; 28 used[p]=1; 29 for(int i=1;i<=n;i++) d[i]=min(d[i],d[p]+w[p][i]); 30 } 31 } 32 33 int main(){ 34 int a,b,c; 35 while(scanf("%d %d",&n,&m)!=EOF&&n&&m){ 36 for(int i=1;i<=n;i++){ 37 for(int j=1;j<=n;j++){ 38 if(i==j) w[i][j]=0; 39 else w[i][j]=INF; 40 } 41 } 42 43 for(int i=1;i<=m;i++){ 44 scanf("%d %d %d",&a,&b,&c); 45 w[a][b]=w[b][a]=c; 46 } 47 dijkstra(1); 48 printf("%d ",d[n]); 49 } 50 return 0; 51 }
(2)用邻接表实现
注意记得每次调用dijkstra()时,first[]置为-1
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7 #include<map> 8 #include<set> 9 #include<queue> 10 #include<algorithm> 11 using namespace std; 12 13 typedef long long LL; 14 const int INF = (1<<30)-1; 15 const int mod=1000000007; 16 const int maxn=10005; 17 int d[maxn],used[maxn],firstt[maxn],nextt[maxn]; 18 int n,m,ecnt; 19 20 struct edge{ 21 int v,cost; 22 } e[maxn]; 23 24 void dijkstra(int s){ 25 memset(used,0,sizeof(used)); 26 for(int i=1;i<=n;i++) d[i]=INF; 27 d[s]=0; 28 29 for(int k=1;k<=n;k++){ 30 int p,m=INF; 31 for(int i=1;i<=n;i++) if(!used[i]&&d[i]<m) m=d[p=i]; 32 used[p]=1; 33 for(int i=firstt[p];i!=-1;i=nextt[i]){ 34 int v=e[i].v; 35 if(d[v]>d[p]+e[i].cost) 36 d[v]=d[p]+e[i].cost; 37 } 38 } 39 } 40 41 void addedges(int u,int v,int w){ 42 nextt[++ecnt]=firstt[u]; 43 e[ecnt].v=v; 44 e[ecnt].cost=w; 45 firstt[u]=ecnt; 46 } 47 48 int main(){ 49 int a,b,c; 50 while(scanf("%d %d",&n,&m)!=EOF&&n&&m){ 51 ecnt=0; 52 memset(firstt,-1,sizeof(firstt)); 53 54 for(int i=1;i<=m;i++){ 55 scanf("%d %d %d",&a,&b,&c); 56 addedges(a,b,c); 57 addedges(b,a,c); 58 } 59 dijkstra(1); 60 printf("%d ",d[n]); 61 } 62 return 0; 63 }
(3)堆(优先队列)优化
参看紫书上的解释,STL中的pair是专门将两个类型捆绑起来的,
而且pair定义了它自己的排序规则,先比较第一维,相等时才比较第二维,所以需要按照(d[i],i)的顺序组合。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7 #include<map> 8 #include<set> 9 #include<queue> 10 #include<algorithm> 11 #define MP(a,b) make_pair(a,b) 12 using namespace std; 13 14 typedef long long LL; 15 typedef pair<int,int> pii; 16 const int INF = (1<<30)-1; 17 const int mod=1000000007; 18 const int maxn=100005; 19 int d[maxn],firstt[maxn],nextt[maxn]; 20 int n,m,ecnt; 21 22 struct edge{ 23 int v,cost; 24 friend bool operator < (edge a,edge b){ 25 return a.cost>b.cost; 26 } 27 }e[maxn]; 28 29 void addedges(int u,int v,int c){ 30 nextt[++ecnt]=firstt[u]; 31 e[ecnt].v=v; 32 e[ecnt].cost=c; 33 firstt[u]=ecnt; 34 } 35 36 void dijkstra(int s){ 37 priority_queue<pii> pq; 38 for(int i=1;i<=n;i++) d[i]=INF; 39 d[s]=0; 40 pq.push(MP(d[s],1)); 41 while(!pq.empty()){ 42 pii x=pq.top();pq.pop(); 43 if(d[x.second]<x.first) continue;//当前状态没有现在dis[]数组里面的优,所以不用再继续判断下去 44 for(int i=firstt[x.second];i!=-1;i=nextt[i]){ 45 int v=e[i].v; 46 if(d[v]>d[x.second]+e[i].cost){ 47 d[v]=d[x.second]+e[i].cost; 48 pq.push(MP(d[v],v)); 49 } 50 } 51 } 52 } 53 54 int main(){ 55 int a,b,c; 56 while(scanf("%d %d",&n,&m)!=EOF&&n&&m){ 57 memset(firstt,-1,sizeof(firstt)); 58 ecnt=0; 59 for(int i=1;i<=m;i++){ 60 scanf("%d %d %d",&a,&b,&c); 61 addedges(a,b,c); 62 addedges(b,a,c); 63 } 64 dijkstra(1); 65 printf("%d ",d[n]); 66 } 67 return 0; 68 }
(4)用vector存图实现的
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7 #include<map> 8 #include<set> 9 #include<queue> 10 #include<algorithm> 11 using namespace std; 12 13 typedef long long LL; 14 const int INF = (1<<30)-1; 15 const int mod=1000000007; 16 const int maxn=10005; 17 int d[maxn]; 18 19 typedef struct edge{ 20 int to,distance; 21 edge(){} 22 edge(int to,int distance) :to(to),distance(distance){} 23 }; 24 typedef pair<int,int> P; 25 vector<edge> G[maxn]; 26 int n,m,k; 27 28 void dijkstra(int s){ 29 30 priority_queue<P,vector<P> ,greater<P> > q; 31 for(int i=0;i < n;i++) d[i]=INF; 32 d[s]=0; 33 q.push(P(0,s)); 34 35 while(!q.empty()){ 36 P p=q.top();q.pop(); 37 int v=p.second; 38 if(d[v]<p.first) continue; 39 for(int i=0;i<G[v].size();i++){ 40 edge e=G[v][i]; 41 42 if(d[e.to] > d[v]+e.distance){ 43 44 d[e.to] = d[v]+e.distance; 45 q.push(P(d[e.to],e.to)); 46 } 47 } 48 } 49 } 50 51 int main(){ 52 while(scanf("%d %d",&n,&m)!=EOF){ 53 if(n==0&&m==0) break; 54 for(int i=0;i<n;i++) G[i].clear(); 55 while(m--){ 56 int u,v,w; 57 scanf("%d %d %d",&u,&v,&w); 58 u--;v--; 59 G[u].push_back(edge(v,w)); 60 G[v].push_back(edge(u,w)); 61 } 62 dijkstra(0); 63 printf("%d ",d[n-1]); 64 } 65 66 return 0; 67 }
2.Bellman_Ford算法
原理:对于图G(V,E),Bellman_Ford是对整个图进行V-1次松弛,每次松弛检查每条边,如果d[v]>d[u]+cost,那么则更新d[v]
应用:可以用来判断负环, 如果没有负权回路的话,那么任意两点间的路径最多经过n-2个点,
即最多松弛n-1次,如果有负权回路,那么第n次松弛操作仍然会成功。
复杂度:O(VE)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7 #include<map> 8 #include<set> 9 #include<queue> 10 #include<algorithm> 11 using namespace std; 12 13 typedef long long LL; 14 const int INF = (1<<30)-1; 15 const int mod=1000000007; 16 const int maxn=100005; 17 int d[maxn]; 18 int n,m,ecnt; 19 20 struct edge{ 21 int u,v,cost; 22 } e[maxn]; 23 24 void addedges(int u,int v,int c){ 25 e[++ecnt].u=u; 26 e[ecnt].v=v; 27 e[ecnt].cost=c; 28 } 29 30 void Bellman_Ford(int s){ 31 for(int i=1;i<=n;i++) d[i]=INF; 32 d[s]=0; 33 34 for(int i=1;i<n;i++){ 35 for(int j=1;j<=ecnt;j++){ 36 if(d[e[j].v]>d[e[j].u]+e[j].cost) 37 d[e[j].v]=d[e[j].u]+e[j].cost; 38 } 39 } 40 } 41 42 int main(){ 43 int a,b,c; 44 while(scanf("%d %d",&n,&m)!=EOF&&n&&m){ 45 ecnt=0; 46 for(int i=1;i<=m;i++){ 47 scanf("%d %d %d",&a,&b,&c); 48 addedges(a,b,c); 49 addedges(b,a,c); 50 } 51 Bellman_Ford(1); 52 printf("%d ",d[n]); 53 } 54 return 0; 55 }