感想:
感想?不敢想不敢想。
刷了一整个最短路专题,其中大部分都是kuangbin带我飞系列的题
期间写了无数个神奇的bug
也曾经通宵调bug(摸鱼)过,暑训的快感来源于此
为了避免之后再踩同样的坑、总结出了一点点模板
还有自己对最短路里面各个算法的一点点体会
1.floyd
目测是最简单的最短路算法吧
3个for 的复杂度爆炸的算法
不过用来写一些数据量特别小的题还是特别舒服的
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const int maxn = 1005; int dis[maxn][maxn]; void floyd(){ for(int k=0;k<n;k++){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(dis[i][j]>dis[i][k]+dis[k][j]){ dis[i][j]=dis[i][k]+dis[k][j]; } } } } } /* floyd的邻接矩阵的用法 n表示顶点个数 dis[i][j]表示从点i到点j的权值大小 复杂度为O(n^3),emmm数据在100、200之间的题可以用 作为最短路入门了 */
2.dijkstra
这个就稍微有难度了
一开始都是对着板子写题,其实刷题刷多了,这个算法的套路也了解了
复杂度是E*O(V)的 边乘以点
经过优先队列的优化后甚至可以达到nlog(n)的级别
思想也很简单,像最小生成树的kruskal的算法一样,我们都是从起始点出发
找距离最短/最长的边,加入队列,一个个进行比较的
最后可以打出来一个dis的表(emmm大佬别打我,我理解的dis是一个表,如果有错,麻烦指出
这个dis的表的意义就是从起点到各个点的最短/最长的距离的集合
于是我们就可以得到单源每一个点到各个点的最短路
所以dijkstra适合用于单源最短路的情况
但是不适合有负权环的情况
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const int maxn = 1005; int mp[maxn][maxn] bool vis[maxn]; int dis[maxn]; int dijkstra(int st,int ed){ vis[st]=1; for(int i=1;i<=n;i++){ dis[i]=mp[1][i]; } int x,minn; for(int i=1;i<=n;i++){ minn=INF; for(int j=1;j<=n;j++){ if(!vis[j]&&minn>dis[j]){ minn=dis[j]; x=j; } vis[x]=1; for(int j=1;j<=n;j++)}{ if(dis[j]>dis[x]+mp[x][j]){ dis[j]=dis[k]+mp[x][j]; } } } return dis[ed]; } /* dijsktra的邻接矩阵的用法, n表示顶点个数 其中mp是邻接矩阵的存图, vis是是否走过的标记 dis是从起点出发到点i距离的最小/最大值 mp、dis初始化为INF 注意题目如果有重边的话需要处理一下 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const int maxn = 1005; typedef pair<double,int>P; int dis[maxn]; bool vis[maxn]; void dijkstra(int st){ memset(dis,INF,sizeof(dis)); memset(vis,0,sizeof(vis)); priority_queue(P,vector<P>,greater<P> ) q; d[st]=0; q.push(P(0,st)); while(!q.empty()){ int x=p.top().second; q.pop(); if(vis[x]) continue; vis[x] = 1; for(int i = 1; i <= n; i++) { if(dis[i] > dis[x] + mp[x][i]) { dis[i] = dis[x] + mp[x][i]; q.push(make_pair(dis[i], i)); } } } }
3.spfa
spfa和bfs算法很像、把可以走的点丢进队列里面,然后每一个都走到不能走为止
然后就可以得到最短路了
因为是从起点到后面所有的点,可以适用于多源最短路的处理情况
我暂时实现了普通的队列版本、手动模拟队列的版本、堆栈的版本待更新
(听说手写堆会特别快
我建图习惯前向星建图(不容易被POJ卡
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const int maxn = 1e5+5; struct node{ int to,next,w; }edge[maxn]; int head[maxn]; bool vis[maxn]; int t=0; void add(int u,int v,int w){ edge[t].to=v; edge[t].w=w; edge[t].next=head[u]; head[u]=t++; } void spfa(int st){ //spfa还可以用堆栈、模拟队列来写,貌似会快一些 memset(vis,0,sizeof(vis)); memset(dis,INF,sizeof(dis)); queue<int> q; q.push(st); vis[st]=1; dis[st]=0; while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(dis[v]>dis[u]+edge[i].w){ dis[v]=dis[u]+edge[i].w; } if(!vis[v]){ vis[v]=1; q.push(v); } } } } /* spfa算法 其中n代表顶点个数 dis[i]代表从起点出发到达i点的最短/最长(依据实际情况而定)距离 vis[i]代表i这个点是否走过 用邻接表的前向星写法有助于存储边多点少的图 用head表示上一个节点 输入之前记得将head初始化为-1 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const int maxn = 1e5+5; struct node{ int to,next,w; }edge[maxn]; int head[maxn]; int q[maxn]; //用数组来模拟队列貌似会快一点点 bool vis[maxn]; int t=0; void add(int u,int v,int w){ edge[t].to=v; edge[t].w=w; edge[t].next=head[u]; head[u]=t++; } void spfa(int st){ memset(vis,0,sizeof(vis)); memset(dis,INF,sizeof(dis)); queue<int> q; q.push(st); vis[st]=1; dis[st]=0; q[0]=st; int top=1; while(top!=0){ top--; int u=q[top]; vis[u]=0; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(dis[v]>dis[u]+edge[i].w){ dis[v]=dis[u]+edge[i].w; } if(!vis[v]){ vis[v]=1; q[top++]=v; } } } } /* spfa算法用模拟队列实现 其实就只是用数组模拟队列 稍微改了一点点,随缘减少常数吧应该还是 嘤嘤嘤 */
最短路最重要的还是建图
要有读题后把题目抽象成图形的能力
所以需要多刷题 遇见各种各样类型的题目才好
学习kuangbin大神的
人一我十,人十我万!
不停的奋斗吧,给凌晨的自己一碗鸡汤