图论总结(6)最短路问题

来源:互联网 发布:网络自制综艺节目 编辑:程序博客网 时间:2024/05/17 03:22

最短路就是求图中两点的最短距离

最短路的算法这里写三种

一.Floyed-Warshall算法(O(N^3))

这是求最短路最简单的算法(求出所有点的最短路,适用于负权边,但不适用与负权回路)

int G[maxn][maxn],pre[maxn][maxn];int n;void print(int x,y){if(pre[x][y]==0)return;print(x,pre[x][y]);cout<<"->"<<y;}void floyed(){for(int k=1;k<=n;i++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i!=j&&j!=k&&k!=j)if(d[i][j]>d[i][k]+d[k][j]){d[i][j]=d[i][k]+d[k][j];pre[i][j]=pre[k][j];}}
邻接矩阵储存,pre记录路径,注意将d数组初始化为一个很大的数。

还可以用floyed来判断连通性。

void floyed(){for(int k=1;k<=n;i++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i!=j&&j!=k&&k!=j)d[i][j]=d[i][j]||(d[i][k]&&d[k][j]);} 

二.Dijkstra(O(N^2))

单源最短路。

不能处理负权边。

算法原理就不讲了,教科书上有,直接上模板

(可以用优先队列优化,这里就不优化了)

int w[maxn][maxn],pre[maxn];int n,s,e;bool b[maxn],d[maxn];void print(int x){if(pre[x]==0)return ;print(pre[x]);cout<<"->"<<x;}void Dijkstra(){memset(b,0,sizeof(b));for(int i=1;i<=n;i++)d[i]=INF;d[s]=0;for(int i=1;i<=n;i++){int minl=INF;int k=0;for(int j=1;j<=n;j++)if(!b[j]&&(d[j]<minl)){minl=d[j];k=j;}if(k==0)break;b[k]=true;for(int j=1;j<=n;j++)if(d[k]+w[k][j]<d[j]){d[j]=d[k]+w[k][j];pre[j]=k;}}}

我反正一般不用Dijkstra算法,觉得BellmanFord更好,特别是优化了过后。

三.Bellman-Ford算法O(NE)

单源最短路 

可以处理负权边,不能处理负权回路。

直接给优化成SPFA常用的算法。

模板:

struct Edge{int from,to,dist;Edge(int f,int t,int d):from(f),to(t),dist(d){}};vector<Edge>edges;vector<int>G[maxn];int vis[maxn],d[maxn],cnt[maxn],pre[maxn],n;void add_Edge(int f,int t,int d){edges.push_back(Edge(f,t,d));int m=edges.size();G[f].push_back(m-1);}bool Bellman_Ford(int s){queue<int>q;memset(cnt,0,sizeof(cnt));memset(vis,0,sizeof(vis));for(int i=1;i<=n;i++)d[i]=INF;d[s]=0;vis[s]=true;q.push(s);while(!q.empty()){int u=q.front();vis[u]=false;for(int i=0;i<G[u].size();i++){Edge& e=edges[G[u][i]];if(d[u]<INF&&d[e.to]>d[u]+e.dist){d[e.to]=d[u]+e.dist;pre[e.to]=G[u][i];if(!vis[e.to]){vis[e.to]=1;q.push(e.to);if(++cnt[e.to]>n)return false;}}}}return true;}
用cnt数组判断是否有负权回路,应为每个点入队了n次时则有负权回路