NO2——最短路径
【Dijkstra算法】
- 复杂度O(n2)
- 权值必须非负
1 /* 求出点beg到所有点的最短路径 */ 2 // 邻接矩阵形式 3 // n:图的顶点数 4 // cost[][]:邻接矩阵 5 // pre[i]记录beg到i路径上的父结点,pre[beg]=-1 6 // 返回:各点的最短路径lowcost[]以及路径pre[] 7 const int maxn=1010; 8 const int INF=0x3f3f3f3f; //防止后面溢出,这个不能太大 9 bool vis[maxn]; 10 int pre[maxn]; 11 void Dijkstra(int cost[][maxn],int lowcost[],int n,int beg) 12 { 13 for(int i=0;i<n;i++) //点的编号从0开始 14 { 15 lowcost[i]=INF;vis[i]=false;pre[i]=-1; 16 } 17 lowcost[beg]=0; 18 for(int j=0;j<n;j++) 19 { 20 int k=-1; 21 int Min=INF; 22 for(int i=0;i<n;i++) 23 if(!vis[i]&&lowcost[i]<Min) 24 { 25 Min=lowcost[i]; 26 k=i; 27 } 28 if(k==-1)break; 29 vis[k]=true; 30 for(int i=0;i<n;i++) 31 if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i]) 32 { 33 lowcost[i]=lowcost[k]+cost[k][i]; 34 pre[i]=k; 35 } 36 } 37 }
【Dijkstra算法+堆优化】
- 复杂度O(E*logE)
- 使用优先队列优化Dijkstra算法
1 //注意对vector<Edge>E[MAXN]进行初始化后加边 2 const int INF=0x3f3f3f3f; 3 const int maxn=1000010; 4 struct qnode 5 { 6 int v; 7 int c; 8 qnode(int _v=0,int _c=0):v(_v),c(_c){} 9 bool operator <(const qnode &r)const 10 { 11 return c>r.c; 12 } 13 }; 14 struct Edge 15 { 16 int v,cost; 17 Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} 18 }; 19 vector<Edge>E[maxn]; 20 bool vis[maxn]; 21 int dist[maxn]; 22 void Dijkstra(int n,int start) //点的编号从1开始 23 { 24 memset(vis,false,sizeof(vis)); 25 for(int i=1;i<=n;i++)dist[i]=INF; 26 priority_queue<qnode>que; 27 while(!que.empty())que.pop(); 28 dist[start]=0; 29 que.push(qnode(start,0)); 30 qnode tmp; 31 while(!que.empty()){ 32 tmp=que.top(); 33 que.pop(); 34 int u=tmp.v; 35 if(vis[u])continue; 36 vis[u]=true; 37 for(int i=0;i<E[u].size();i++){ 38 int v=E[tmp.v][i].v; 39 int cost=E[u][i].cost; 40 if(!vis[v]&&dist[v]>dist[u]+cost){ 41 dist[v]=dist[u]+cost; 42 que.push(qnode(v,dist[v])); 43 } 44 } 45 } 46 } 47 void addedge(int u,int v,int w) 48 { 49 E[u].push_back(Edge(v,w)); 50 }
【Bellman-ford算法】
- 复杂度O(V*E)
- 可以处理负边权图
1 //可以判断是否存在负环回路 2 //返回true,当且仅当图中不包含从源点可达的负权回路 3 //vector<Edge>E; 4 //先E.clear()初始化,然后加入所有边 5 const int INF=0x3f3f3f3f; 6 const int maxn=550; 7 int dist[maxn]; 8 struct Edge 9 { 10 int u,v; 11 int cost; 12 Edge(int _u=0,int _v=0,int _cost=0):u(_u),v(_v),cost(_cost){} //构造函数 13 }; 14 vector<Edge>E; 15 bool bellman_ford(int start,int n) //点的编号从1开始 16 { 17 for(int i=1;i<=n;i++)dist[i]=INF; 18 dist[start]=0; 19 for(int i=1;i<n;i++) //最多做n-1次 20 { 21 bool flag=false; 22 for(int j=0;j<E.size();j++) 23 { 24 int u=E[j].u; 25 int v=E[j].v; 26 int cost=E[j].cost; 27 if(dist[v]>dist[u]+cost) 28 { 29 dist[v]=dist[u]+cost; 30 flag=true; 31 } 32 } 33 if(!flag)return true; //没有负环回路 34 } 35 for(int j=0;j<E.size();j++) 36 if(dist[E[j].v]>dist[E[j].u]+E[j].cost) 37 return false; //有负环回路 38 return true; //没有负环回路 39 }
【SPFA算法】
- 复杂度O(K*E)
1 //这个是队列实现,有时候改成栈实现会更加快,很容易修改 2 //这个复杂度是不定的 3 const int maxn=1010; 4 const int INF=0x3f3f3f3f; 5 struct Edge 6 { 7 int v; 8 int cost; 9 Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} 10 }; 11 vector<Edge>E[maxn]; 12 void addedge(int u,int v,int w) 13 { 14 E[u].push_back(Edge(v,w)); 15 } 16 bool vis[maxn]; //在队列标志 17 int cnt[maxn]; //每个点的入队列次数 18 int dist[maxn]; 19 bool SPFA(int start,int n) 20 { 21 memset(vis,false,sizeof(vis)); 22 for(int i=1;i<=n;i++)dist[i]=INF; 23 vis[start]=true; 24 dist[start]=0; 25 queue<int>que; 26 while(!que.empty())que.pop(); 27 que.push(start); 28 memset(cnt,0,sizeof(cnt)); 29 cnt[start]=1; 30 while(!que.empty()) 31 { 32 int u=que.front(); 33 que.pop(); 34 vis[u]=false; 35 for(int i=0;i<E[u].size();i++) 36 { 37 int v=E[u][i].v; 38 if(dist[v]>dist[u]+E[u][i].cost) 39 { 40 dist[v]=dist[u]+E[u][i].cost; 41 if(!vis[v]) 42 { 43 vis[v]=true; 44 que.push(v); 45 if(++cnt[v]>n)return false; //cnt[i]为入队列次数,用来判定是否存在负环回路 46 } 47 } 48 } 49 } 50 return true; 51 }
【Floyd-Warshall算法】
- 复杂度O(n3)
- 边权非负
1 #include<cstdio> 2 using namespace std; 3 #define INF 1e9 4 const int maxn=100+10; 5 int n,m; //点数,边数,点从0到n-1编号 6 int dist[maxn][maxn]; //记录距离矩阵 7 int path[maxn][maxn]; //path[i][j]=x表示i到j的路径上(除i外)的第一个点是x. 8 void init() 9 { 10 for(int i=0;i<n;i++) 11 for(int j=0;j<n;j++) 12 { 13 dist[i][j] = i==j ? 0 : INF; //其实这里d[i][j]应该还要通过输入读数据的 14 path[i][j] = j; 15 } 16 17 //读取其他dist[i][j]的值 18 } 19 void floyd() 20 { 21 for(int k=0;k<n;k++) 22 for(int i=0;i<n;i++) 23 for(int j=0;j<n;j++) 24 if(dist[i][k]<INF && dist[k][j]<INF ) 25 { 26 if(dist[i][j]>dist[i][k]+dist[k][j]) 27 { 28 dist[i][j] = dist[i][k]+dist[k][j]; 29 path[i][j] = path[i][k]; 30 } 31 else if(dist[i][j] == dist[i][k]+dist[k][j] &&path[i][j]>path[i][k]) 32 { 33 path[i][j] = path[i][k]; //最终path中存的是字典序最小的路径 34 } 35 } 36 } 37 38 int main() 39 { 40 //读n和m 41 init(); 42 //读m条边 43 floyd(); 44 //输出所求最短路径距离 45 return 0; 46 }