[P3371 ]【模板】单源最短路径
这道题是模板题,有很多种做法。我使用Bellman-Ford、spfa、floyd算法来做练习,积累些经验。
难点:
1.无穷会因为负权边松弛,即INF+w<INF .
例如:设dis为到点1的最短路的长度,dis[2]=INF,dis[3]=INF,存在边(2,3)=-2,除此之外没有以3为终点的边
则根据dis[v]>dis[u]+w,dis[3]=INF-2!=INF,这将导致dis[3]理论上结果为无穷,但是实际数值不为INF的情况。
为避免这种松弛导致判断不可达该点时, dis[i]!=INF但i点实际不可达的情况,采取特判dis[u]不为无穷,避免松弛。
int u=edges[i].u,v=edges[i].v,w=edges[i].w;if(dis[u]!=INF&&dis[v]>dis[u]+w)/*无穷会因为负权边松弛,即INF+w<INF .为避免这种松弛导致判断不可达该点时,dis[i]!=INF但i点实际不可达的情况,采取特判dis[u]不为无穷,避免松弛。 */{dis[v]=dis[u]+w;}
2.若不能到达则输出 231-1。这个数据在Int范围内,但是表示时需要注意移位运算与减法运算的优先级
long long INF=(1<<31)-1;//减法优先级高于移位,需要加括号提高优先级
整体代码如下,没有考虑优化,三个TLE。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=5e5+10, INF=(1<<31)-1;//减法优先级高于移位,需要加括号提高优先级
int idx;
long long dis[N];
struct edge{int u,v,w;//出边、权值、下一出边
};
edge edges[M];
int n,m,u,v,w;
void add(int u,int v,int w){edges[idx].u=u;edges[idx].v=v;edges[idx].w=w;idx++;
}
void BF(){for(int i=0;i<n-1;i++)//传递n-1次,避免负环 {for(int i=0;i<m;i++){int u=edges[i].u,v=edges[i].v,w=edges[i].w;if(dis[u]!=INF&&dis[v]>dis[u]+w)/*无穷会因为负权边松弛,即INF+w<INF .为避免这种松弛导致判断不可达该点时,dis[i]!=INF但i点实际不可达的情况,采取特判dis[u]不为无穷,避免松弛。 */{dis[v]=dis[u]+w;}}}
}
int main(){int s;cin>>n>>m>>s;for(int i=0;i<m;i++){//录入边, cin>>u>>v>>w;add(u,v,w);}for(int i=1;i<=n;i++)dis[i]=INF;//该无穷大无法用类似0x3f赋值 dis[s]=0;//源点最短路初始化 BF();for(int i=1;i<n;i++)cout<<dis[i]<<' ';cout<<dis[n]<<endl;return 0;
}
优化
某轮未发生松弛,则接下来的传递必然不会发生松弛。
方案:采用对每轮标记是否发生松弛,来判定结束时机。结束时机要么是所有轮迭代完,要么是某轮未发生松弛
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=5e5+10, INF=(1<<31)-1;//减法优先级高于移位,需要加括号提高优先级
int idx;
long long dis[N];
struct edge{int u,v,w;//出边、权值、下一出边
};
edge edges[M];
int n,m,u,v,w;
void add(int u,int v,int w){edges[idx].u=u;edges[idx].v=v;edges[idx].w=w;idx++;
}
void BF(){for(int i=0;i<n-1;i++)//传递n-1次,避免负环 { int check=0; //每轮都需要初始标记 for(int i=0;i<m;i++){int u=edges[i].u,v=edges[i].v,w=edges[i].w;if(dis[u]!=INF&&dis[v]>dis[u]+w)/*无穷会因为负权边松弛,即INF+w<INF .为避免这种松弛导致判断不可达该点时,dis[i]!=INF但i点实际不可达的情况,采取特判dis[u]不为无穷,避免松弛。 */{dis[v]=dis[u]+w;check=1;//松弛则标记该轮传递有 }}if(!check)break;//某次传递所有点都没有松弛,则说明所有可能的松弛已经结束。 }
}
int main(){int s;cin>>n>>m>>s;for(int i=0;i<m;i++){//录入边, cin>>u>>v>>w;add(u,v,w);}for(int i=1;i<=n;i++)dis[i]=INF;//该无穷大无法用类似0x3f赋值 dis[s]=0;//源点最短路初始化 BF();for(int i=1;i<n;i++)cout<<dis[i]<<' ';cout<<dis[n]<<endl;return 0;
}
spfa是上面算法的队列优化版本
思想:被松弛的终点v可以作为下一次传递的起点u,来尝试下一次传递。
由于松弛后v的最短路路长被修改,那么它的所有出度结点的最短路都会受到影响。好比一波激起千层浪。
故而通过将被松弛节点v入队列的方式维护它的出度,维护完毕后则该节点v出队。如果被松弛的终点v在队列中,显然它正等待维护它的出度。由于终点被松弛后会影响它的出度,但只会因为两点关联的边而发生可能的松弛,由关联的定义得维护出度只需要一次传递即可,因此被松弛且队列的终点v不需要再次入队,否则就是浪费时间。
显然是一种贪心策略:通过确定当前结点v的最短路路长,来维护v的所有出度的最短路。
代码如下
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=5e5+10;
const long long INF=(1<<31)-1;
int n,m,s;
long long dis[N];
int vis[N],idx,h[N],ne[M],e[M],to[M];
void add(int u,int v,int w){ne[idx]=h[u];to[idx]=v;e[idx]=w;h[u]=idx++;
}
void spfa(){for(int i=1;i<=n;i++)dis[i]=INF;dis[s]=0;queue<int> q;q.push(s);while(!q.empty()){int t=q.front();q.pop();vis[t]=0;for(int i=h[t];~i;i=ne[i]){int v=to[i],w=e[i];if(dis[t]!=INF&&dis[v]>dis[t]+w){dis[v]=dis[t]+w;if(!vis[v]){//被松弛且未在队列q中的汇点入队 vis[v]=1;q.push(v);}} }}
}
int main(){freopen("test.txt","r",stdin);cin>>n>>m>>s;memset(h,-1,sizeof h);for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;add(a,b,c);}spfa();for(int i=1;i<n;i++)cout<<dis[i]<<' ';cout<<dis[n]<<endl;return 0;
}
floyd算法
采用floyd求解会超时。
而且本题的重边会导致邻接矩阵算法WA。重边的应对方案是记录其中权值最小的边。
for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;if(maze[u][v]>w)//应对本题的重边 maze[u][v]=w;}
本题仍需要松弛,因此需要判断松弛对象是否为INF,避免松弛无路顶点的情况
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,INF=(1<<31)-1;
int maze[N][N];
int n,m,s;
void floyd(){for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(maze[k][j]!=INF&&maze[i][k]!=INF&&maze[i][j]>maze[i][k]+maze[k][j]){maze[i][j]=maze[i][k]+maze[k][j];}}
}
int main(){freopen("test.txt","r",stdin);cin>>n>>m>>s;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i!=j)maze[i][j]=INF;else maze[i][j]=0;for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;if(maze[u][v]>w)//应对本题的重边 maze[u][v]=w;}floyd();for(int i=1;i<n;i++){cout<<maze[s][i]<<' ';}cout<<maze[s][n]<<endl;return 0;
}
[P3371 ]【模板】单源最短路径相关推荐
- P3371 【模板】单源最短路径(弱化版)
题目 P3371 [模板]单源最短路径(弱化版) 分析 Dijkstra模板题,只不过这里用了链式前向星 AC代码 #include<cstdio> #include<iostrea ...
- 洛谷 P3371 【模板】单源最短路径(弱化版)【最短路】【spfa】
洛谷 P3371 [模板]单源最短路径(弱化版) 一.题目链接 二.题目分析 (一)算法标签 (二)解题思路 三.AC代码 四.其它题解 一.题目链接 洛谷 P3371 [模板]单源最短路径(弱化版) ...
- p3371 单源最短路径(弱化版)-java题解-最短路
弱化版传送门: P3371 [模板]单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通 ...
- P4779 【模板】单源最短路径(标准版)
# [模板]单源最短路径(标准版) ## 题目背景 2018 年 7 月 19 日,某位同学在 [NOI Day 1 T1 归程](https://www.luogu.org/problemnew/s ...
- Luogu 3371【模板】单源最短路径
Luogu 3371[模板]单源最短路径 第一次写博客用图论题来试一试 接下来是正文部分 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入输出格式 输入格式: 第一行包 ...
- 最短路——【模板】单源最短路径(弱化版)(dijkstra)
题目链接 最短路--[模板]单源最短路径(弱化版)(dijkstra) 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入格式 第一行包含三个整数 n,m,s,分别表示点 ...
- 洛谷_P3371 【模板】单源最短路径(弱化版)_dijkstra_堆优化
洛谷_P3371 [模板]单源最短路径(弱化版)_dijkstra_堆优化 // dijkstra最短路算法_堆优化 #include<bits/stdc++.h> using names ...
- 单源最短路径(最短路)
洛谷--P3371 [模板]单源最短路径(spfa) 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入输出格式 输入格式: 第一行包含三个整数N.M.S,分别表示点的个 ...
- Dijkstra算法求解单源最短路径问题
文章目录 一 前言 二 Dijkstra 算法讲解 1. 贪心算法的证明 2. 算法实现说明 3. 初版Dijkstra算法代码 三 时间复杂度优化 1. 优化策略 2. 优化后的代码 四 结语 一 ...
- p4779 单源最短路径(标准版)-java版
传送门: P4779 [模板]单源最短路径(标准版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P4779 先给 ...
最新文章
- 高压放电与防静电塑料包装
- 编译有哪些阶段,动态链接和静态链接的区别 c++
- 如何让 Mybatis 自动生成代码
- graphpad如何检测方差齐_如何选择方差分析中“多重比较”的方法?
- 用CSS3来代替JS实现交互
- 华为Mate 40 Pro维修价来了:这个部件最贵,够买一部顶级旗舰
- 对Gson解析的理解
- (16)数据结构-并查集
- C语言项目源码2022必看必学版
- 分布式面试题(二):分布式Redis
- 高频分类ISO1443, ISO15693, ISO18000-3
- 类似print shopmail可变数据生成,排版、拼版实现
- 个人朋友圈时代过去了,企业微信朋友圈开辟营销新思路
- java speex转码_JAVA版-微信语音.speex转.wav
- LeetCode——5776. 判断矩阵经轮转后是否一致(Determine Whether Matrix Can Be Obtained By Rotation)[简单]——分析及代码(Java)
- 用c语言编程一个英尺转换器,C语言中关于英尺、英寸、厘米的换算
- 移动端电影院社交来啦 约上ta看一场电影
- 如何从Google Play下载应用到电脑
- java method field_java_解析Java中的Field类和Method类,Field类
Field类中定义了一些方 - phpStudy...
- 126邮箱OUTLOOKS设置