最短路就是求图中两点的最短距离
最短路的算法这里写三种
一.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次时则有负权回路