单源最短路径经常与DFS、DP、二分、拓扑排序等算法的结合使用。
题目列表:

题目 算法
AcWing 1135. 新年好 最短路+DFS
AcWing 340. 通信线路 二分+双端队列BFS
AcWing 342. 道路与航线 最短路+拓扑排序+DFS
AcWing 341. 最优贸易 最短路、正图和反图

一、最短路 + DFS

AcWing 1135. 新年好

本题为单源多终点最短路,一共有5个终点,从起点1到这5个点有5!种方案确定先到哪一个点。我们可以dfs求得全排列然后求5!次的最短路,时间复杂度偏高O(5!∗mlogn)O(5! * mlogn)O(5!∗mlogn)。
因此我们可以反过来做。先分别以这6个点为起点跑6次最短路,然后再dfs得到最短路径的距离,这样的时间复杂度为O(5!+mlogn)O(5!+mlogn)O(5!+mlogn)。

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> PII;const int N = 50010, M = 500100, INF = 0x3f3f3f3f;int n, m, k;int dis[6][N];
bool vis[N];
int ver[M],nex[M],head[N],edge[M],tot;
int source[6];void add(int x,int y,int z){ver[++tot] = y;edge[tot] = z;nex[tot] = head[x];head[x] = tot;
}void dijkstra(int S,int dist[]){memset(dist,0x3f,N * 4);memset(vis,0,sizeof vis);priority_queue<PII,vector<PII>,greater<PII> >q;dist[S] = 0;q.push({0,S});while(q.size()){int x = q.top().second;q.pop();if(vis[x])continue;vis[x] = true;for(int i = head[x];~i;i = nex[i]){int y = ver[i],z = edge[i];if(dist[y] > dist[x] + z){dist[y] = dist[x] + z;q.push({dist[y],y});}}}
}int dfs(int x,int cnt,int distance){if(cnt > 5)return distance;int res = INF;for(int i = 1;i <= 5;i ++ ){if(!vis[i]){int y = source[i];vis[i] = true;res = min(res,dfs(i,cnt + 1,distance + dis[x][y]));vis[i] = false;//回溯}}return res;
}int main(){memset(head,-1,sizeof head);scanf("%d%d",&n,&m);source[0] = 1;for(int i = 1;i <= 5;i++)scanf("%d",&source[i]);while(m--){int x,y,z;scanf("%d%d%d",&x,&y,&z);add(x,y,z),add(y,x,z);}for(int i = 0;i <= 5;++i)//dij6次dijkstra(source[i],dis[i]);memset(vis,0,sizeof vis);//初始化一次printf("%d\n",dfs(0,1,0));//从0(编号)开始,第1个,距离为0return 0;
}

二、分层图最短路 / 二分 + 双端队列BFS

AcWing 340. 通信线路

本题实际上是要求最短路中的第k + 1大值最小化。
最大值最小化问题很明显可以用二分来解决

注意二分的边界要设置为从0到1e6+1,从0开始是因为答案可能为0,到1e6+1结束是因为本题中如果没有可行的解就输出-1,而数据的范围是到1e6,因此如果二分的答案是1e6+1那么就意味着答案无解。
具体解析:
来源

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> PII;const int N = 2010, M = 50010, INF = 0x3f3f3f3f;int n, m, k;int dist[N];
bool vis[N];
int ver[M],nex[M],head[N],edge[M],tot;
deque<int>q;void add(int x,int y,int z){ver[++tot] = y;edge[tot] = z;nex[tot] = head[x];head[x] = tot;
}bool check(int bound){memset(dist,0x3f,sizeof dist);memset(vis,0,sizeof vis);q.push_back(1);dist[1] = 0;while(q.size()){int x = q.front();q.pop_front();if(vis[x])continue;vis[x] = true;for(int i = head[x];~i;i = nex[i]){int y = ver[i],z = edge[i] > bound;//z是指的该边是否大于当前二分的值,所以只有0和1两种值,所以用双端队列if(dist[y] > dist[x] + z){dist[y] = dist[x] + z;if(!z)q.push_front(y);else q.push_back(y);}}}return dist[n] <= k;
}int main(){cin>>n>>m>>k;memset(head,-1,sizeof head);while(m--){int x,y,z;scanf("%d%d%d",&x,&y,&z);add(x,y,z),add(y,x,z);}int l = 0,r = 1e6 + 1;while(l < r){int mid = (l + r) >> 1;if(check(mid))r = mid;else l = mid + 1;}if(l == 1e6 + 1)puts("-1");else cout<<r<<endl;return 0;
}

三、AcWing 342. 道路与航线

3.1、最短路 + 拓扑排序 + dfs查找连通块(正解)

本题是团内正权值,团间负权值。

y总的思路:

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> PII;const int N = 25010, M = 150010, INF = 0x3f3f3f3f;int n, m, s, mp;int ver[M],nex[M],head[N],edge[M],tot;
int id[N];
int dist[N],din[N];
vector<int>block[N];
int bcnt;
bool vis[N];
queue<int>q;void add(int x,int y,int z){ver[++tot] = y;edge[tot] = z;nex[tot] = head[x];head[x] = tot;
}void dfs(int u, int bid){//dfs求连通块id[u] = bid, block[bid].push_back(u);for(int i = head[u]; ~i; i = nex[i]){int v = ver[i];if(!id[v])dfs(v, bid);}
}void dijkstra(int bid){priority_queue<PII, vector<PII>, greater<PII> > heap;for(auto u : block[bid])heap.push({dist[u], u});while(heap.size()){auto t = heap.top();heap.pop();int x = t.second, distance = t.first;if(vis[x])continue;vis[x] = true;for(int i = head[x]; ~i; i = nex[i]){int y = ver[i], z = edge[i];if(id[x] != id[y] && --din[id[y]] == 0)q.push(id[y]);if(dist[y] > dist[x] + z){dist[y] = dist[x] + z;if(id[x] == id[y])heap.push({dist[y], y});}}}
}void toposort(){memset(dist, 0x3f, sizeof dist);dist[s] = 0;for(int i = 1; i <= bcnt; ++i){if(!din[i])//topo排序,先把入度为0的放进去q.push(i);}while(q.size()){int t = q.front();q.pop();dijkstra(t);}
}int main(){scanf("%d%d%d%d",&n, &m, &mp, &s);memset(head, -1, sizeof head);while(m--){int x,y,z;scanf("%d%d%d",&x, &y, &z);add(x,y,z),add(y,x,z);}for(int i = 1; i <= n; ++i)if(!id[i])dfs(i,++bcnt);while(mp--){int x, y, z;scanf("%d%d%d",&x, &y, &z);din[id[y]] ++ ;//统计入度add(x,y,z);}toposort();for(int i = 1; i <= n; ++i){//因为有负权值边,所以只要>INF/2即为没有通路if(dist[i] > INF / 2)puts("NO PATH");else cout<<dist[i]<<endl;}return 0;
}

3.2、SPFA的SLF优化(取巧)

SLF优化:

SLF优化,即 Small Label First 策略,使用 双端队列 进行优化。

一般可以优化15%~20%,在竞赛中比较常用。

众所周知,SPFA算法是一种鉴于队列的实现算法.每一次有节点加入队列都是加入队尾.
但是SLF优化,不同于一般的SPFA算法,它是一种利用双端队列算法处理的问题.
如果说当前点所花费的值少于我们当前队头点的值的话,那么我们就将这个节点插入到队头去,否则我们还是插入到队尾.(每次取队头)
这个就是非常好用的SLF优化算法.

设从 u 扩展出了 v ,队列中队首元素为 k ,若 dis[ v ] < dis[ k ] ,则将 v 插入队首,否则插入队尾。

注:队列为空时直接插入队尾。

#include <bits/stdc++.h>
using namespace std;
const int N=400000 +100;
int head[N],ver[N],Next[N],edge[N],vis[N],dis[N],tot;
void add_edge(int a,int b,int c)
{edge[tot]=b;ver[tot]=c;Next[tot]=head[a];head[a]=tot++;
}
void spfa(int s)
{memset(dis,0x3f,sizeof(dis));memset(vis,false,sizeof(vis));deque<int> q;dis[s]=0;vis[s]=true;q.push_back(s);while(q.size()){int now=q.front();vis[now]=false;q.pop_front();for(int i=head[now]; ~i; i=Next[i]){int j=edge[i];if (dis[j]>dis[now]+ver[i]){dis[j]=dis[now]+ver[i];if (!vis[j]){vis[j]=true;SLF优化实际上只是增加了这两句判断if (q.size() && dis[j]<dis[q.front()])q.push_front(j);///elseq.push_back(j);}}}}
}
int main()
{int t,r,p,s,x,y,z;ios::sync_with_stdio(false);cin.tie(0);cin>>t>>r>>p>>s;memset(head,-1,sizeof(head));for(int i=1; i<=r; i++){cin>>x>>y>>z;add_edge(x,y,z);add_edge(y,x,z);}for(int i=1; i<=p; i++){cin>>x>>y>>z;add_edge(x,y,z);}spfa(s);for(int i=1; i<=t; i++){if (dis[i]==0x3f3f3f3f)cout<<"NO PATH"<<endl;elsecout<<dis[i]<<endl;}return 0;
}

3.3、SPFA的LLL优化

SLF表示小的优先,LLL表示大的最后,那么什么样的的dis值是大的呢?难道还和队首元素比较吗?当然不是,是于队列的平均数来比较,如果大于这个平均数就放到最后。

void SPFA()
{memset(dis,inf,sizeof(dis));queue<int>q;q.push(1);dis[1]=0;vis[1]=1;while(q.size()){p=q.front();q.pop();if(dis[p]*cnt_2>sum){q.push(p);continue;}sum-=dis[p];cnt_2--;vis[p]=0;for(int i=head[p];i;i=map[i].next){s=map[i].to;if(dis[s]>dis[p]+map[i].value){dis[s]=dis[p]+map[i].value;if(vis[s]==0){vis[s]==1;q.push(s);cnt_2++;sum+=dis[s];}}}}
}

3.4、SPFA的DFS优化(适用于负环的判断)

这种优化顾名思义,就是用dfs的思想代替bfs的思想来优化Bellman-Ford。

常常用于判断正/负环,时间复杂度可以达到O(m)(m是边)。思路是,我们每一次dfs的时候如果走回之前dfs过的点,那就是有环,除了这个dfs的标记,我们还可以打另一个vis数组记录更新过权值的节点,以后就不必重复更新,大大降低复杂度。

不过如果无环的话,那还是上面那两种优化稍微适用一点。代码比较短,但是不好扩展。

inline bool spfa(int x)
{dfs[x]=1;for(int i=head[x];i;i=g[i].next){int y=g[i].ver,z=g[i].edge;if(!v[y]||d[y]<d[x]+z){if(dfs[y]) return 0;v[y]=1;d[y]=d[x]+z;if(!spfa(y)) return 0;}}dfs[x]=0;return 1;
}

四、luogu P1073 (NOIP2009)最优贸易


#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<queue>#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
#define lowbit(p) p&(-p)
using namespace std;typedef long long ll;
typedef pair<int,int> PII;const int INF = 0x3f3f3f3f;
const int N = 1e5+7;
const int M = 2e6+7;int h[N],rh[N],ver[M],nex[M],tot;
int dmin[N],dmax[N];
int price[N];
bool vis[N];
int n,m;void init(){memset(h,-1,sizeof h);memset(rh,-1,sizeof rh);
}void add(int *h,int u,int v){ver[tot] = v;nex[tot] = h[u];h[u] = tot++;
}void spfa(int *dis,int s,int *arr,bool flag){memset(vis,0,sizeof vis);if(flag)memset(dis,0x3f,sizeof dmin);//注意这一点,已经两次了queue<int>q;q.push(s);vis[s] = 1;dis[s] = price[s];while(q.size()){int u = q.front();q.pop();vis[u] = 0;for(int i = arr[u];~i;i = nex[i]){int v = ver[i];if((flag && dis[v] > min(dis[u],price[v])) || (!flag && dis[v] < max(dis[u],price[v]))){if(flag)dis[v] = min(dis[u],price[v]);else dis[v] = max(dis[u],price[v]);if(!vis[v])q.push(v),vis[v] = 1;}}}
}int main()
{init();scanf("%d%d",&n,&m);over(i,1,n)scanf("%d",&price[i]);over(i,1,m){int x,y,z;scanf("%d%d%d",&x,&y,&z);add(h,x,y),add(rh,y,x);//反图应该是反边if(z == 2)add(h,y,x),add(rh,x,y);}spfa(dmin,1,h,true);spfa(dmax,n,rh,false);int ans = 0;over(i,1,n)ans = max(ans,dmax[i]-dmin[i]);printf("%d\n",ans);return 0;
}

【图论专题】单源最短路的综合应用相关推荐

  1. 算法提高课-图论-单源最短路的综合应用-AcWing 1135. 新年好:dijkstra和dfs暴搜结合

    题目分析 来源:acwing 分析: 先预处理出从1,a,b,c,d,e出发到其他所有点的单源最短路.存在二维数组dist[6][N]中 dfs暴搜所有拜访顺序,共有5!种,对于每一种拜访顺序,可以通 ...

  2. [AcWing算法提高课]之图论 单源最短路的综合应用(C++题解)

    目录 1)热浪(板子题) (朴素dijkstra) O(n2) (堆优化dijkstra) O((n+m)logm) (spfa) O(m) 2)信使 3)香甜的黄油 4)最小花费 5) 最优乘车 6 ...

  3. 算法提高课-图论-单源最短路的综合应用-AcWing 342. 道路与航线:最短路dijkstra、拓扑排序 、综合题、好题

    题目分析 来源:acwing 分析: 道路:双向,边权非负, 航线:单向,边权可正可负,且无环. 根据题意,点可以分为很多团(连通块),团内部只有道路(道路是双向的,而且是连通的,所以不能存在航线,否 ...

  4. Acwing 1126. 最小花费【图论、单源最短路】

    在 n 个人中,某些人的银行账号之间可以互相转账. 这些人之间转账的手续费各不相同. 给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问 A 最少需要多少钱使得转账后 B 收到 100 元 ...

  5. 算法提高课-图论-差分约束- AcWing 1169. 糖果:spfa求单源最短路、差分约束

    文章目录 题目分析 题目链接 题目分析 来源:acwing 分析: 差分约束系统 差分约束系统是一种特殊的N元一次不等式组.它包含N个变量X1,...,XnX_1,...,X_nX1​,...,Xn​ ...

  6. 算法提高课-图论-单源最短路的建图方式-AcWing 920. 最优乘车:bfs求最短路、建图

    题目分析 来源:acwing 分析: 本题难在抽象建图上,这里采用的建图方式是:同一条公交线路上,前面的站点都可以连一条有向边到其后面的站点,且边权都为1. 由于边权都是1,可以用bfs来求最短路. ...

  7. 单/全源最短路的综合应用(未完待续...)

    目录 一.单源最短路 1.1 AcWing 1129. 热浪 1.2 AcWing 1128. 信使 1.3 AcWing 1127. 香甜的黄油 1.4 AcWing 1126. 最小花费 1.5 ...

  8. acwing单源最短路的建图模式总结

    .根据边权的范围以及问题求解的需要,最短路问题可以分为以下 4 种情形,分别用不同的算法求解. • 单源最短路径(固定一个顶点为原点,求源点到其他每个顶点 的最短路径) • 1. 边权非负:Dijks ...

  9. 邻接矩阵和邻接表_[力扣743] 带权邻接表的单源最短路

    题目链接 743. 网络延迟时间 题目描述 有 N 个网络节点,标记为 1 到 N. 给定一个列表 times,表示信号经过有向边的传递时间. times[i] = (u, v, w),其中 u 是源 ...

最新文章

  1. CSS高度塌陷问题-清除浮动
  2. Xcode7.2如何真机调试iOS 9.3的设备
  3. android-6.0不支持FloatMath.sqrt(x * x + y * y)
  4. asp.net Mvc 使用NPOI导出Excel文件
  5. k8s滚动升级_k8s deployment 滚动更新
  6. python自编信息加密函数_自定义Python加密算法
  7. 精品资源:40个实用的 PSD 贴纸模板《下篇》
  8. TCP Timestamp选项
  9. 【LeetCode】【字符串】题号:*451. 根据字符出现频率排序
  10. 那智机器人程序备份复原方法
  11. Hexo Next 主题字体相关配置
  12. java计算机毕业设计校园环境保护监督系统源代码+系统+数据库+lw文档
  13. Google Cloud API设计指南
  14. 【洛谷题解】P1042 [NOIP2003 普及组] 乒乓球
  15. 酷比魔方iwork8刷机shell_酷比魔方iwork8旗舰版刷机教程及固件上线
  16. 【研报】供应链流通视角,透视中国商流之变革
  17. 任务栏出现两个重复图标的解决办法
  18. 讲台英语怎么读计算机,一种计算机教学讲台的制作方法
  19. MUR10060CT-ASEMI大功率快恢复模块
  20. 头歌-信息安全-病毒分析与防御

热门文章

  1. 收藏 | 深度学习在计算机视觉领域的应用总结
  2. 详解OpenCV卷积滤波之边缘处理与锚定输出
  3. (原創) 如何將編譯結果,統一放在一個目錄下? (SOC) (Quartus II)
  4. 使用apidocJs快速生成在线文档
  5. 交换机多生成树协议MSTP
  6. 通过案例学功能 自定义监控功能初探
  7. rrdtool 修改水印logo
  8. [WebService]之代码优先方法与契约优先方法
  9. 在VMware ESX Server使用华为存储
  10. date string 转化为weekday