本周学习了最小生成树和最短路、贪心,并做了一些最短路的题目。虽然本次题目不多,但我还是没有能够全部A掉,只是A了一小部分。
Dijkstra算法(解决单源最短路径)
它所处理的图不能有负权。基本思路:对得到邻接矩阵,也就是不同位置的关系表示图,通过dijkstra我们能求出1到其他的点的最短路径,通过数组dis[ ]来记下初始化状态下1到其他点的距离,先从权值最小的点开始,搜索他周围能到达的所有的点的路径,将1经过这个点到达他们的总路径相加,与dis[ ]数组中的相对于的点进行比较,如果距离小与dis[]数组中的距离,就更新dis中的距离,在这个离1最近的点找完后,我们再去找下一个离源点近的点,再遍历它周围的所有点,每次找到的离源点最近的点后,都要去标记它已经被使用过了,下次遍历的寻找的时候就不能再 用它了,这样反复。
例题:https://www.luogu.com.cn/problem/P1339
题意:有一个n个点m条边的无向图,求出从s到t的最短路长度。
dijkstra算法,一个最短路径的模板题,对于像我这样的小白去理解算法的实现有很大的帮助。
#include<bits/stdc++.h>
using namespace std;
int arr[3000][3000];//建立一个储存点对应关系的数组
int vis[3000];//建立一个记录是否走过的数组
int dis[3000];//建立一个记录最小值的数组
int main()
{
memset(arr,60,sizeof(arr));
memset(vis,0,sizeof(vis));
memset(dis,60,sizeof(dis));
int n,m,s,t;
cin>>n>>m>>s>>t;
int u,v,w;
for(int a=0;a<m;a++)
{
cin>>u>>v>>w;
arr[u][v]=w;
arr[v][u]=w;
}
dis[s]=0;//将开始点的距离设为0;
for(int a=0;a<n;a++)
{
int min=0;
for(int b=1;b<=n;b++)
if(dis[min]>dis[b]&&vis[b]==0)
min=b;
vis[min]=1;//将用过的点标记
//比较这个点到其他点是否可以更新距离
for(int b=1;b<=n;b++)
if(dis[min]+arr[min][b]<dis[b])
dis[b]=dis[min]+arr[min][b];
}
//打印开始点到结束点的距离
cout<<dis[t];
}
Spfa算法(解决负权边)
如果一个邻接矩阵中存在负权环,bellman也是可以计算的,先假设它是正权环,进行循环n-1次,循环的n-1次就是正权环有n个顶点的话就最多有n-1条边,这个算法就是来枚举每条边的数据,它的操作会根据所输入的边的次序来进行,每次按所输入边的顺序全部遍历一遍,然后将符合条件的进行操作,知道达到要求的效果,但如果遇到负权环的话,进行了n-1次操作后,再次对这个图进行Ford算法的操作时,发现还可以操作的。所以可以在结尾判断一下,如果可以继续操作的话,那就说它是没有最短路径的。
Floyd算法(解决多源最短路径)
基本思路:如果要求两个点之间的最短距离,不一定是两个点之间的直达距离,有可能是经过转点后的距离,例如从A->B的距离为10,而从A->C的距离为5,从C->B的距离为4,如果走A->C->B的距离的话,那么就更短,如果确定这条路径是最短距离,那么也同样可以确定,A->C的最短路径为5,B->C的最短路径为4,也就是说,如果确定了一条多转站的路径为最短路径后,它所经过的点都为从一点到沿途点的最短路径。
例题:https://www.luogu.com.cn/problem/P2888
题意:想找一条路径从Ai到Bi,使路径上最高的栏的高度最小。
FLOYD算法,枚举每个点,算出总和,同时找距离和最小的一个点记录下标。
#include<bits/stdc++.h>
using namespace std;
int n,m,t,dis[1000][1000],a,b,c,x,y;
int main(){
cin>>n>>m>>t;
memset(dis,0x7f,sizeof(dis));
for(int i=1;i<=m;i++){
cin>>a>>b>>c;
dis[a][b]=c;
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int s=max(dis[i][k],dis[k][j]);
if(s!=0x7f7f7f7f&&s<dis[i][j]){
dis[i][j]=s;
}
}
}
}
for(int i=1;i<=t;i++){
cin>>x>>y;
if(dis[x][y]==0x7f7f7f7f){
cout<<-1<<endl;
continue;
}
cout<<dis[x][y]<<endl;
}
return 0;
}
总结:在这些题目中大部分都是用dijkstra和floyd算法解决的。Dijkstra算法解决单源最短路径,不能解决负权边;Floyd算法解决多源最短路径,时间复杂度高;Spfa算法可以解决负权边。虽然说大部分都是模板题,但是也有很多找不到头绪理解不透题意的题目,看起题目来很懵。