SPFA+SPFA优化+优先队列(汽车加油行驶问题_铺垫aa、汽车加油行驶问题qiche、EasySSSPsssp)

原文

https://fmcraft.top/index.php/Programming/13.html

主要算法

最短路径快速算法SPFA
最短路径快速算优化SPFAO
优先队列PriorityQueue

题目列表

P1:汽车加油行驶问题(辅垫)aa

题目描述

题目描述

给定一个 N×N 的方形网格,设其左上角为起点◎,坐标为 (1,1) ,X 轴向右为正, Y 轴向下为正,每个方格边长为 1 ,如图所示。

一辆汽车从起点◎出发驶向右下角终点▲,其坐标为 (N,N)。

在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则:

汽车只能沿网格边行驶,装满油后能行驶 K 条网格边。出发时汽车已装满油,在起点与终点处不设油库。

汽车经过一条网格边时,若其 X 坐标或 Y 坐标减小,则应付费用 B ,否则免付费用。

汽车在行驶过程中遇油库则应加满油并付加油费用 A。

在需要时可在网格点处增设油库,并付增设油库费用 C (不含加油费用 A )。

N,K,A,B,C 均为正整数, 且满足约束: 2≤N≤100,2≤K≤10。

设计一个算法,求出汽车从起点出发到达终点的一条所付费用最少的行驶路线。

输入

第一行是 N,K,A,B,C的值。

第二行起是一个N×N 的 0-1 方阵,每行 N 个值,至 N+1 行结束。

方阵的第 i 行第 j 列处的值为 1 表示在网格交叉点 (i,j) 处设置了一个油库,为 0 时表示未设油库。各行相邻两个数以空格分隔。
输出

输出所有边的信息(起点的行、列、油到终点的行、列、油和这条边的权值)

注意:必须按照上下左右,先走后加油的顺序进行输出。
样例输入

bash 复制代码
2 2 10 5 5
0 1
1 0

样例输出

bash 复制代码
1 1 0 1 1 2 15
1 1 1 2 1 0 0
1 1 1 1 2 0 0
1 1 1 1 1 2 15
1 1 2 2 1 1 0
1 1 2 1 2 1 0
1 2 0 1 2 2 10
1 2 1 1 2 2 10
1 2 2 2 2 1 0
1 2 2 1 1 1 5
2 1 0 2 1 2 10
2 1 1 2 1 2 10
2 1 2 1 1 1 5
2 1 2 2 2 1 0
2 2 0 2 2 2 15
2 2 1 1 2 0 5
2 2 1 2 1 0 5
2 2 1 2 2 2 15
2 2 2 1 2 1 5
2 2 2 2 1 1 5

数据范围

2≤n≤100

2≤k≤10

题解报告

初始化与输入:首先读取输入的参数,包括网格大小 N、油箱容量 K、加油费用 A、行驶费用 B 和增设油库费用 C。接着读取网格信息,判断每个网格点是否有油库。

遍历网格:使用三重循环遍历网格的每个点 (i, j) 和油箱容量 k。外层循环遍历网格的行,中层循环遍历网格的列,内层循环遍历油箱容量。

判断油库:在内层循环中,首先判断当前点是否有油库。如果有油库,并且油箱已满 (k == K),则调用函数 d() 遍历四个方向并输出行驶信息。否则,输出加油信息。

行驶与加油:如果没有油库,则判断油箱是否为空 (k == 0)。如果为空,则不输出行驶信息。否则,调用函数 d() 遍历四个方向并输出行驶信息。如果油箱未满,则输出增设油库信息。

输出结果:根据题目要求,输出所有边的信息,包括起点的行、列、油到终点的行、列、油和这条边的权值。

标准程序
c 复制代码
#include <bits/stdc++.h>
using namespace std;
const int e[4][3]={{-1,0,1},{1,0,0},{0,-1,1},{0,1,0}},N=110;
int n,K,A,B,C,i,j,k,h,sx,sy,a[N][N];
void d(){
	for(h=0;h<4;h++){
		sx=i+e[h][0];sy=j+e[h][1];
		if(!(sx<1||sx>n||sy<1||sy>n))
            cout<<i<<" "<<j<<" "<<k<<" "<<sx<<" "<<sy<<" "<<k-1<<" "<<e[h][2]*B<<"\n";
	}
}
int main(){
	cin>>n>>K>>A>>B>>C;
	for(i=1;i<=n;i++) for(j=1;j<=n;j++) cin>>a[i][j];
	for(i=1;i<=n;i++) for(j=1;j<=n;j++) for(k=0;k<=K;k++)
		if(a[i][j])
			if(k==K) d();
			else cout<<i<<" "<<j<<" "<<k<<" "<<i<<" "<<j<<" "<<K<<" "<<A<<"\n";
		else{
			if(k>0) d();
			if(k<K) cout<<i<<" "<<j<<" "<<k<<" "<<i<<" "<<j<<" "<<K<<" "<<A+C<<"\n";
		}
}

P2:汽车加油行驶问题qiche

题目描述

题目描述

给定一个 N×N 的方形网格,设其左上角为起点◎,坐标为 (1,1) ,X 轴向右为正, Y 轴向下为正,每个方格边长为 1 ,如图所示。

一辆汽车从起点◎出发驶向右下角终点▲,其坐标为 (N,N)。

在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则:

汽车只能沿网格边行驶,装满油后能行驶 K 条网格边。出发时汽车已装满油,在起点与终点处不设油库。

汽车经过一条网格边时,若其 X 坐标或 Y 坐标减小,则应付费用 B ,否则免付费用。

汽车在行驶过程中遇油库则应加满油并付加油费用 A。

在需要时可在网格点处增设油库,并付增设油库费用 C (不含加油费用 A )。

N,K,A,B,C 均为正整数, 且满足约束: 2≤N≤100,2≤K≤10。

设计一个算法,求出汽车从起点出发到达终点的一条所付费用最少的行驶路线。

输入

第一行是 N,K,A,B,C的值。

第二行起是一个N×N 的 0-1 方阵,每行 N 个值,至 N+1 行结束。

方阵的第 i 行第 j 列处的值为 1 表示在网格交叉点 (i,j) 处设置了一个油库,为 0 时表示未设油库。各行相邻两个数以空格分隔。
输出

程序运行结束时,输出最小费用。
样例输入 复制

bash 复制代码
9 3 2 3 6
0 0 0 0 1 0 0 0 0
0 0 0 1 0 1 1 0 0
1 0 1 0 0 0 0 1 0
0 0 0 0 0 1 0 0 1
1 0 0 1 0 0 1 0 0
0 1 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0 1
1 0 0 1 0 0 0 1 0
0 1 0 0 0 0 0 0 0

样例输出 复制

bash 复制代码
12

提示

数据范围:

2≤n≤100

2≤k≤10

题解报告

初始化与输入:首先读取输入的参数,包括网格大小 N、油箱容量 K、加油费用 A、行驶费用 B 和增设油库费用 C。接着读取网格信息,判断每个网格点是否有油库。

遍历网格:使用三重循环遍历网格的每个点 (i, j) 和油箱容量 k。外层循环遍历网格的行,中层循环遍历网格的列,内层循环遍历油箱容量。

判断油库:在内层循环中,首先判断当前点是否有油库。如果有油库,并且油箱已满 (k == K),则调用函数 d() 遍历四个方向并输出行驶信息。否则,输出加油信息

行驶与加油:如果没有油库,则判断油箱是否为空 (k == 0)。如果为空,则不输出行驶信息。否则,调用函数 d() 遍历四个方向并输出行驶信息。如果油箱未满,则输出增设油库信息。

输出结果:根据题目要求,输出最小费用。

标准程序(无记忆化搜索,速度慢)
c 复制代码
#include <bits/stdc++.h>
using namespace std;
const int e[4][3]={{-1,0,1},{1,0,0},{0,-1,1},{0,1,0}},N=200010,M=110;
int n,K,A,B,C,i,j,k,h,sx,mi,sy,sg,fx,fb,a[M][M],dis[N];
vector <int> f[N],g[N];
priority_queue <pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
int haha(int i,int j,int k){return k*1e4+(i-1)*100+j;}
void d1(){
	for(h=0;h<4;h++){
		sx=i+e[h][0];sy=j+e[h][1];
		if(!(sx<1||sx>n||sy<1||sy>n))
			f[haha(i,j,k)].push_back(haha(sx,sy,k-1)),
			g[haha(i,j,k)].push_back(e[h][2]*B);
	}
}
void d2(int X){
	f[haha(i,j,k)].push_back(haha(i,j,K));
	g[haha(i,j,k)].push_back(X==1?A:A+C);
}
int main(){
	cin>>n>>K>>A>>B>>C;
	for(i=1;i<=n;i++) for(j=1;j<=n;j++) cin>>a[i][j];
	for(i=1;i<=n;i++) for(j=1;j<=n;j++) for(k=0;k<=K;k++)
		if(a[i][j]) if(k==K) d1();
					else d2(1);
		else{
			if(k>0) d1();
			if(k<K) d2(2);
		}
	for(i=1;i<=haha(n,n,k);i++) dis[i]=2e9;
	dis[haha(1,1,K)]=0;
	q.push(make_pair(0,haha(1,1,K)));
	while(!q.empty()){
		fx=q.top().second;fb=q.top().first;q.pop();
		for(i=0;i<f[fx].size();i++){
			sx=f[fx][i];sg=g[fx][i];
			if(sg+dis[fx]<dis[sx]) dis[sx]=dis[fx]+sg,q.push(make_pair(dis[sx],sx));
		}
	}
	for(mi=2e9,i=0;i<=K;i++) mi=min(mi,dis[haha(n,n,i)]);
	cout<<mi;
}
标准程序(记忆化搜索,速度更快)
c 复制代码
#include <bits/stdc++.h>
using namespace std;
const int e[4][3]={{-1,0,1},{1,0,0},{0,-1,1},{0,1,0}},N=200010,M=110;
int n,K,A,B,C,i,j,k,h,sx,mi,sy,sg,fx,fb,a[M][M],dis[N],vis[N];
vector <int> f[N],g[N];
priority_queue <pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
int haha(int i,int j,int k){return k*1e4+(i-1)*100+j;}
void d1(){
	for(h=0;h<4;h++){
		sx=i+e[h][0];sy=j+e[h][1];
		if(!(sx<1||sx>n||sy<1||sy>n))
			f[haha(i,j,k)].push_back(haha(sx,sy,k-1)),
			g[haha(i,j,k)].push_back(e[h][2]*B);
	}
}
void d2(int X){
	f[haha(i,j,k)].push_back(haha(i,j,K));
	g[haha(i,j,k)].push_back(X==1?A:A+C);
}
int main(){
	cin>>n>>K>>A>>B>>C;
	for(i=1;i<=n;i++) for(j=1;j<=n;j++) cin>>a[i][j];
	for(i=1;i<=n;i++) for(j=1;j<=n;j++) for(k=0;k<=K;k++)
		if(a[i][j]) if(k==K) d1();
					else d2(1);
		else{
			if(k>0) d1();
			if(k<K) d2(2);
		}
	for(i=1;i<=haha(n,n,k);i++) dis[i]=2e9,vis[i]=1;
	dis[haha(1,1,K)]=vis[haha(1,1,K)]=0;
	q.push(make_pair(0,haha(1,1,K)));
	while(!q.empty()){
		fx=q.top().second;fb=q.top().first;vis[fx]=1;q.pop();
		for(i=0;i<f[fx].size();i++){
			sx=f[fx][i];sg=g[fx][i];
			if(sg+dis[fx]<dis[sx]){
				dis[sx]=dis[fx]+sg;
				if(vis[sx]==1)
                    q.push(make_pair(dis[sx],sx)),vis[sx]=0;
			}
		}
	}
	for(mi=2e9,i=0;i<=K;i++) mi=min(mi,dis[haha(n,n,i)]);
	cout<<mi;
}

P3:EasySSSPsssp

题目描述

题目描述、输入、输出

输入数据给出一个有 N 个节点,M 条边的带权有向图。要求你写一个程序,判断这个有向图中是否存在负权回路。如果从一个点沿着某条路径出发,又回到了自己,而且所经过的边上的权和小于 0,就说这条路是一个负权回路。

如果存在负权回路,只输出一行 −1;如果不存在负权回路,再求出一个点S到每个点的最短路的长度。约定:S 到 S 的距离为 0,如果 S 与这个点不连通,则输出 NoPath
样例输入 复制

bash 复制代码
6 8 1
1 3 4
1 2 6
3 4 -7
6 4 2
2 4 5
3 6 3
4 5 1
3 5 4

样例输出 复制

bash 复制代码
0
6
4
-3
-2
7

提示

数据范围:

对于全部数据,2≤N≤1000,1≤M≤105,1≤a,b,S≤N,∣c∣≤106。

做这道题时,你不必为超时担心,不必为不会算法担心,但是如此「简单」的题目,你究竟能 AC 么?

解题思路
  1. 创建一个邻接表来表示有向图。
  2. 初始化一个距离数组 dist,将源节点 S 的距离设为 0,其他节点的距离设为无穷大。
  3. 进行 Bellman-Ford 算法的松弛操作,遍历所有边,更新每个节点的距离。
  4. 检查是否存在负权回路。如果在进行 n-1 次松弛操作后,仍然存在可以更新的距离,则说明存在负权回路。
  5. 如果存在负权回路,输出 -1
  6. 如果不存在负权回路,则进行一次松弛操作,得到最短路径。
  7. 遍历所有节点,如果距离为无穷大,则输出 *NoPath*,否则输出距离
标准程序
c 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
int i,n,m,s,x,y,z,fx,fb,sx,sg,dis[N],vis[N],fl[N],ans[N];
vector <int> f[N],g[N];
priority_queue <pair<int,int>,vector<pair<int,int> > ,greater<pair<int,int> > > q;
void spfa(int st){
	int i;
	for(i=1;i<=n;i++) dis[i]=2e9,vis[i]=1;dis[st]=vis[st]=0;
	q.push(make_pair(0,st));
	while(!q.empty()){
		fx=q.top().second;fb=q.top().first;vis[fx]=1;q.pop();fl[fx]++;
		if(fl[fx]>n) cout<<-1,exit(0);
		for(i=0;i<f[fx].size();i++){
			sx=f[fx][i];sg=g[fx][i];
			if(dis[fx]+sg<dis[sx]){
				dis[sx]=dis[fx]+sg;
				if(vis[sx]==1) q.push(make_pair(dis[sx],sx));
			}
		}
	}
	if(st==s) for(i=1;i<=n;i++) ans[i]=dis[i];
}
signed main(){
	cin>>n>>m>>s;
	for(i=1;i<=m;i++) cin>>x>>y>>z,
		f[x].push_back(y),g[x].push_back(z);
	spfa(s);
	for(i=1;i<=n;i++) if(fl[i]==0) spfa(i);
	for(i=1;i<=n;i++) if(ans[i]==2e9) cout<<"NoPath\n";
					  else cout<<ans[i]<<"\n";
}

总结

以上几个例题整体围绕最短路径算法展开,通过不同题目练习,让学员掌握最短路径算法在实际问题中的应用,理解算法的优化和实现细节,提升解决复杂图论问题的能力 。

相关推荐
爱数模的小驴28 分钟前
2025 年“认证杯”数学中国数学建模网络挑战赛 C题 化工厂生产流程的预测和控制
深度学习·算法·计算机视觉
序属秋秋秋2 小时前
算法基础_数据结构【单链表 + 双链表 + 栈 + 队列 + 单调栈 + 单调队列】
c语言·数据结构·c++·算法
apcipot_rain3 小时前
【密码学——基础理论与应用】李子臣编著 第五章 序列密码 课后习题
算法·密码学
不要不开心了3 小时前
sparkcore编程算子
pytorch·分布式·算法·pygame
88号技师3 小时前
【2024年最新IEEE Trans】模糊斜率熵Fuzzy Slope entropy及5种多尺度,应用于状态识别、故障诊断!
人工智能·算法·matlab·时序分析·故障诊断·信息熵·特征提取
清同趣科研3 小时前
R绘图|6种NMDS(非度量多维分析)绘图保姆级模板——NMDS从原理到绘图,看师兄这篇教程就够了
人工智能·算法
杜小暑3 小时前
冒泡排序与回调函数——qsort
c语言·算法·排序算法
徵6863 小时前
代码训练day27贪心算法p1
算法·贪心算法
Nigori7_5 小时前
day32-动态规划__509. 斐波那契数__70. 爬楼梯__746. 使用最小花费爬楼梯
算法·动态规划
x_feng_x5 小时前
数据结构与算法 - 数据结构与算法进阶
数据结构·python·算法