- Dijkstra算法
- Floyd算法
- SPFA算法
Dijkstra算法(最经典的单源路径算法-贪心的思想)
问题描述:
数据已在数组中初始化,算出从给定的点到最最终点的一条最短距离。
输出:
最短路径为 16
#include<bits/stdc++.h>
using namespace std;
#define MAXVEX 9
#define M 65535
int p[MAXVEX];//用于存储最短路径下标的数组
int D[MAXVEX];//用于存储到各点最短路径下的权值和
typedef struct{
int numVertexes;
int arc[MAXVEX][MAXVEX];
}MGraph;
void ShortestPath_Dijkstra(MGraph G,int v0)
{
int v,w,k,min;
int final[MAXVEX];//final[w]==1 表示已经求得顶点V0到Vw的最短路径
//初始化数据
for(v=0;v<G.numVertexes;v++)
{
final[v]=0;//全部顶点初始化为未找到最短路径
D[v]=G.arc[v0][v];//将于V0点有连线的顶点加上权值
p[v]=0;//初始化路径数组P为0
}
D[v0]=0;//v0到v0的路径为0
final[v0]=1;//v0到v0不需要求路径
//开始主循环,每次求得v0到某个v顶点的最短路径
for(v=1;v<G.numVertexes;v++)
{
min=M;
for(w=0;w<G.numVertexes;w++)
{
if(!final[w]&&D[w]<min)
{
k=w;//k=8就不再读了
min=D[w];
}
}
cout<<k;
final[k]=1;//将目前找到的最近的顶点置为1
//修正当前最短路径及距离
for(w=0;w<G.numVertexes;w++)
{
//如果经过V顶点的路径比现在这条路径的长度短的话,更新!
if(!final[w]&&(min+G.arc[k][w]<D[w]))
{
D[w]=min+G.arc[k][w];//修正当前路径长度
p[w]=k;
cout<<"-"<<D[w];
}
}
}
cout<<endl;
}
int main()
{
MGraph G;
int i,j;
G.numVertexes=MAXVEX;
int a[MAXVEX][MAXVEX]={
{0,1,5,M,M,M,M,M,M},
{1,0,3,7,5,M,M,M,M},
{5,0,3,M,1,7,M,M,M},
{M,7,M,0,2,M,3,M,M},
{M,5,1,2,0,3,6,9,M},
{M,M,7,M,3,0,M,5,M},
{M,M,M,3,6,M,0,2,7},
{M,M,M,M,9,5,2,0,4},
{M,M,M,M,M,M,7,4,0},
};
for(i=0;i<MAXVEX;i++)
for(j=0;j<MAXVEX;j++)
G.arc[i][j]=a[i][j];
for(i=0;i<MAXVEX;i++)//验证数组中的值
{
for(j=0;j<MAXVEX;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
ShortestPath_Dijkstra(G,0);
cout<<"P的过程为:";//处理过程
for(i=0;i<MAXVEX;i++)
{
if(i==MAXVEX-1)
cout<<p[i]<<endl;
else
cout<<p[i]<<"->";
}
cout<<"D的过程为:";
for(i=0;i<MAXVEX;i++)
{
if(i==MAXVEX-1)
cout<<D[i]<<endl<<"最短路径为:"<<D[i]<<endl;
else
cout<<D[i]<<"->";
}
return 0;
}
Floyd算法(经典的多元最短路径算法-动态规划的思想)
问题描述:
数据已在数组中初始化,算出从每个点到最最终点的最短距离。
输出:
0–>8 的最短路径为 16
1–>8 的最短路径为 15
2–>8 的最短路径为 12
3–>8 的最短路径为 9
4–>8 的最短路径为 11
5–>8 的最短路径为 9
6–>8 的最短路径为 6
7–>8 的最短路径为 4
#include<bits/stdc++.h>
using namespace std;
#define MAXVEX 9
#define M 65535
int p[MAXVEX][MAXVEX];//用于存储最短路径下标的数组
int D[MAXVEX][MAXVEX];//用于存储到各点最短路径下的权值和
typedef struct{
int numVertexes;
int matrix[MAXVEX][MAXVEX];
}MGraph;
void ShortestPath_Floyd(MGraph G)
{
int v,w,k;
//初始化D和p
for(v=0;v<G.numVertexes;v++)
{
for(w=0;w<G.numVertexes;w++)
{
D[v][w]=G.matrix[v][w];
p[v][w]=w;
}
}
//优美的Floyd算法
for(k=0;k<G.numVertexes;k++)
for(v=0;v<G.numVertexes;v++)
for(w=0;w<G.numVertexes;w++)
{
if(D[v][w]>(D[v][k]+D[k][w]))
{
D[v][w]=D[v][k]+D[k][w];
p[v][w]=p[v][k];
}
}
}
int main()
{
MGraph G;
int i,j;
G.numVertexes=MAXVEX;
int a[MAXVEX][MAXVEX]={
{0,1,5,M,M,M,M,M,M},
{1,0,3,7,5,M,M,M,M},
{5,0,3,M,1,7,M,M,M},
{M,7,M,0,2,M,3,M,M},
{M,5,1,2,0,3,6,9,M},
{M,M,7,M,3,0,M,5,M},
{M,M,M,3,6,M,0,2,7},
{M,M,M,M,9,5,2,0,4},
{M,M,M,M,M,M,7,4,0},
};
for(i=0;i<MAXVEX;i++)//对二维数组进行赋值
for(j=0;j<MAXVEX;j++)
G.matrix[i][j]=a[i][j];
for(i=0;i<MAXVEX;i++)//测试数据
{
for(j=0;j<MAXVEX;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
cout<<"------------------------------"<<endl;
for(i=0;i<MAXVEX;i++)
{
for(j=0;j<MAXVEX;j++)
cout<<" "<<G.matrix[i][j];
cout<<endl;
}
ShortestPath_Floyd(G);
for(i=0;i<G.numVertexes;i++)
{
for(j=0;j<G.numVertexes;j++)
if(j==G.numVertexes-1)
cout<<i<<"-->"<<j<<"的最短路径为"<<D[i][j]<<endl;
}
return 0;
}
SPAF算法(松弛操作)
spfa的本质是:用队列优化的bellman-ford算法。它用队列来进行松弛。每次松弛过后的点,一定要在队列中(判定是否在队列中,如果有,就加入)。
spfa有一些小优化,而堆也是其中的一种。这时它的本质,是一种堆优化的dijkstra算法。
问题描述
给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。
输入格式
第一行两个整数n, m。
接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。
输出格式
共n-1行,第i行表示1号点到i+1号点的最短路。
样例输入
3 3
1 2 -1
2 3 -1
3 1 2
样例输出
-1
-2
数据规模与约定
对于10%的数据,n = 2,m = 2。
对于30%的数据,n <= 5,m <= 10。
对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。
#include <bits/stdc++.h>
using namespace std;
struct edge
{
int to,val,next;
}e[200100];//e[i].to,e[i].val,e[i].next分别表示第i条边的终点,权值,和另(上)一条于此边相同起点的边的编号
int m,n,head[21000],dis[21000];//head[i]表示当前以i为起点的边的编号,dis[i]表示1号点到i号点的距离
void add(int from,int to,int val,int len)//添加边,参数分别代表此边的起点,终点,权值,还有此边的编号
{
e[len].to=to;
e[len].val=val;
e[len].next=head[from];//此边的起点是from,而head[from]保存上一条起点是from的边的编号
head[from]=len;//添加了这条边后,现在,最新的以from为起点的边编号就是len
}
void spfa()
{
int s;
queue <int> q;
int visit[21000];//visit[i]==0代表第i点不在队列中,visit[i]==1代表已在队列中
for(int i=1;i<=n;i++)
{
dis[i]=99999999;
}//将权值初始化为最大值
memset(visit,0,sizeof(visit));//初始没有点在队列中
dis[1]=0;
q.push(1);
visit[1]=1;//现在把第一个点扔进队列
while(!q.empty())//如果队列不空则重复执行以下操作
{
int t=q.front();//取队列的第一个元素
q.pop();
visit[t]=0;//t现在不在队列里
for(int i=head[t];i!=-1;i=e[i].next)//枚举所有以t为起点的边
{
s=e[i].to;//s为所枚举的边的终点
if(dis[s]>dis[t]+e[i].val)//尝试松弛s点
{
dis[s]=dis[t]+e[i].val;
{
dis[s]=dis[t]+e[i].val;
if(visit[s]==0)
{
q.push(s);
visit[s]=1;
}//如果成功松弛了s点,把s点扔进队列
}
}
}
}
}
int main ()
{
cin>>n>>m;
int from,val,to;
int len=1;
memset (head,-1,sizeof(head));
for(int i=0;i<m;i++)
{
cin>>from>>to>>val;
add(from,to,val,len);
len++;
}
spfa();
for(int i=2;i<=n;i++)
{
cout<<dis[i]<<endl;
}
}