最近正好在学最短路径,借此机会把学到的知识总结下,一来分享给大家阅读,二来方便以后自己查阅。
先列一下下面会总结到的求最短路径的算法:
1.Dijkstra算法;
2.Bellman-Ford算法;
3.Folyd算法;
4.SPFA算法;
Dijkstra算法:求单源最短路径(不带负权重的环)
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAXN 10000
const int INF=0x3f3f3f3f;
int n, m, ori;
int dis[MAXN], pre[MAXN];
int cost[MAXN][MAXN];
struct node
{
int v;
int len;
node(){}
node(int vv, int ll):v(vv), len(ll){}
bool operator < (const node& a) const
{
return len > a.len;
}
};
void Dijkstra()
{
for(int i = 1; i <= n; i++)
dis[i] = INF;
priority_queue q;//最小优先队列优化
q.push(node(ori, 0));
dis[ori] = 0;
while(!q.empty())
{
node t;
t = q.top();
q.pop();
int now = t.v;
for(int j = 1; j <= n; j++)
{
if(cost[now][j] != INF && dis[j] > dis[now] + cost[now][j])
{
dis[j] = dis[now] + cost[now][j];
pre[j] = now;
q.push(node(j, dis[j]));
}
}
}
}
void print_path(int pos)
{
printf("%d\n", dis[pos]);
while(ori != pos)
{
printf("%d<---", pos);
pos = pre[pos];
}
printf("%d\n", ori);
}
int main()
{
scanf("%d%d%d", &n, &m, &ori);
int u, v, w;
memset(cost, INF, sizeof(cost));
while(m--)
{
scanf("%d%d%d", &u, &v, &w);
cost[u][v] = w;
cost[v][u] = w;//若是有向图则不要这行
}
Dijkstra();
for(int i = 1; i <= n; i++)
{
printf("%d to %d: ", ori, i);
if(dis[i] != INF)
print_path(i);
else
printf("Have no path\n");
}
return 0;
}
Bellman-Ford算法:求单源最短路径(可带负权重的环)
假设存在G=
step1.初始化,dis[V0]=0,dis[i]=无穷大(i≠V0,表示不可达);
step2.进行循环,循环下标为从0到V-1(V等于图中点的个数),在循环内部,遍历所有的边,进行松弛操作;
step3.遍历图中所有的边(edge(u,v)),判断是否存在这样情况:d(v) > d (u) + w(u,v),存在则返回false,表示图中存在从源点可达的权为负的回路。
#include
#include
#include
#include
using namespace std;
#define MAXN 10000
const int INF = 0x3f3f3f3f;
struct Edge
{
int u, v;//u->v
int w;//距离
};
int n, m, ori;//点数、边数、起始点
int dis[MAXN], pre[MAXN];
vector edge;
bool Bellman_Ford()
{
for(int i = 1; i <= n; i++)
dis[i] = INF;
dis[ori] = 0;
for(int i = 0; i < n-1; i++)
for(int j = 0; j < edge.size(); j++)
{
if(dis[edge[j].v] > dis[edge[j].u] + edge[j].w)
{
dis[edge[j].v] = dis[edge[j].u] + edge[j].w;
pre[edge[j].v] = edge[j].u;
}
}
bool flag = true;
for(int i = 0; i < edge.size(); i++)
{
if(dis[edge[i].v] > dis[edge[i].u] + edge[i].w)
{
flag = false;
break;
}
}
return flag;
}
void print_path(int start)//打印路径
{
printf("%d\n", dis[start]);
while(start != ori)
{
printf("%d<---", start);
start = pre[start];
}
printf("%d\n", ori);
}
int main()
{
scanf("%d%d%d", &n, &m, &ori);
Edge t;
while(m--)
{
scanf("%d%d%d", &t.u, &t.v, &t.w);
edge.push_back(t);
}
if(Bellman_Ford())
{
for(int i = 1; i <= n; i++)
{
printf("%d to %d: ", ori, i);
if(dis[i] != INF)
print_path(i);
else
printf("Have no path\n");
}
}
else
printf("This map has negative circle\n");
return 0;
}
Folyd算法:所有顶点对最短路径(不带负权重的环)
假设存在G=
step1.将所有顶点对之间距离初始化为无穷大(dis[i][j]=无穷大),然后将输入的i到j的距离保存在dis[i][j]中;
step2.依次扫描每一个点,并以其为基点再遍历所有每一对顶点dis[i][j]的值,看看是否可用过该基点让这对顶点间的距离更小。
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAXN 10000
const int INF = 0x3f3f3f3f;
int dis[MAXN][MAXN], path[MAXN][MAXN];
int n, m, ori;
void Floyd()
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
path[i][j] = i;
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
if(dis[i][j] > dis[i][k] + dis[k][j])
{
dis[i][j] = dis[i][k] + dis[k][j];
path[i][j] = path[k][j];
}
}
}
void prin_path()
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
if(i != j)
{
if(dis[i][j] == INF)
printf("have no path\n");
else
{
printf("%d to %d: ", i, j);
printf("%d\n", dis[i][j]);
int k = j;
while(i != k)
{
printf("%d<---", k);
k = path[i][k];
}
printf("%d\n", i);
}
}
}
}
int main()
{
int u,v, w;
scanf("%d%d%d", &n, &m, &ori);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
if(i != j)
dis[i][j] = INF;
else
dis[i][j] = 0;
}
while(m--)
{
scanf("%d%d%d", &u, &v, &w);
dis[u][v] = w;
}
Floyd();
prin_path();
return 0;
}
SPFA算法:单源最短路径(可带负权重的环)
假设存在G=
step1.将所有顶点对之间距离初始化为无穷大(dis[i][j]=无穷大),pre[i]=i,vis[i]=0,将源点入队;
step2.读取队头顶点now,并将队头顶点now出队(记得消除标记),将与点now相连的所有点next进行松弛操作,更新dis[next],另外,如果点next没有在队列中,那么要将点next入队(记得标记),如果已经在队列中了,那么就不用入队(如果某个顶点入队超过V次,则说明图中有负环,直接跳出);
step3.重复step2,直到队空为止就完成了单源最短路的求解。
#include
#include
#include
#include
#include
using namespace std;
#define MAXN 10000
const int INF = 0x3f3f3f3f;
struct Edge
{
int v;
int w;
Edge(){}
Edge(int vv, int ww):v(vv), w(ww){}
};
int dis[MAXN], vis[MAXN], pre[MAXN];
int cnt[MAXN];
int n, m, ori;
vector edge[MAXN];
int SPFA()
{
queue q;
for(int i = 1; i <= n; i++)
{
dis[i] = INF;
vis[i] = 0;
cnt[i] = 0;
pre[i] = ori;
}
dis[ori] = 0;
q.push(ori);
vis[ori]=1;
cnt[ori]++;
while(!q.empty())
{
int now = q.front();
q.pop();
vis[now] = 0;
for(int i = 0; i < edge[now].size(); i++)
{
int next = edge[now][i].v;
if(dis[next] > dis[now] + edge[now][i].w)
{
dis[next] = dis[now] + edge[now][i].w;
pre[next] = now;
if(!vis[next])
{
q.push(next);
vis[next] = 1;
cnt[next]++;
if(cnt[next] > n)
return 0;
}
}
}
}
return 1;
}
void print_path(int pos)
{
printf("%d to %d: ", ori, pos);
printf("%d\n", dis[pos]);
while(ori != pos)
{
printf("%d<---", pos);
pos = pre[pos];
}
printf("%d\n", ori);
}
int main()
{
int u, v, w;
scanf("%d%d%d", &n, &m, &ori);
while(m--)
{
scanf("%d%d%d", &u, &v, &w);
edge[u].push_back(Edge(v, w));
}
if(SPFA())
{
for(int i = 1; i <= n; i++)
if(dis[i] != INF)
print_path(i);
else
printf("Have no path\n");
}
else
printf("This map has negative circle\n");
return 0;
}
Bellman-Ford算法参考:http://blog.csdn.net/niushuai666/article/details/6791765
Folyd算法参考:http://developer.51cto.com/art/201403/433874.htm
SPFA算法参考:http://www.cnblogs.com/scau20110726/archive/2012/11/18/2776124.html