蓝桥杯 ALGO-5 最短路

题目

问题描述
给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。

输入格式
第一行两个整数n, m。

接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。

输出格式
共n-1行,第i行表示1号点到i+1号点的最短路。
样例输入

3 3
1 2 -1
2 3 -1
3 1 2

样例输出

-1
-2

数据规模与约定
对于10%的数据,n = 2,m = 2。

对于30%的数据,n <= 5,m <= 10。

对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。

题解

题目有负的权值,不能用 Dijkstra 算法
SPFA算法
还要优化过
设置队列,每次入队相邻结点,判断距离是否更近,如果距离更近将该相邻结点入队列,直到队列为空。可在相邻结点进行处理,设置数组 head 存储相邻点编号,边存储相同起点的下一边编号,这样找邻接点更快

未优化版本
只有70分,但是相对好理解些

#include<queue>
#include<cstdio>
#include<string.h>
using namespace std;
const int MAXM = 200005;
const int MAXN = 20005;
const int INF = 99999999;
struct Edge{  // 边信息 
	int sta; // 起点 
	int des; // 终点
	int val;  // 权值 
}e[MAXM];
int n; // 点 
int m; // 边 
int dis[MAXN]={0};  // 距离 
bool visit[MAXM] = {false}; // 记录点的访问状态 

void Init(){
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;i++)
		dis[i] = INF;
	for(int i=0;i<m;i++)
		scanf("%d%d%d",&e[i].sta,&e[i].des,&e[i].val);
}

void SPFA(){
	queue<int> q;
	q.push(1);  // 第一个点入队列 
	visit[1] = true;  // 改变访问状态
	int f; // 队列第一个元素
	while(!q.empty()){
		f = q.front();
		q.pop();
		visit[f] = false;  // 已经取出来了
		for(int i=0;i<m;i++){  // 遍历以 f 为起点的所有值 
			if(e[i].sta == f){
				// i 到 j 有边
				// 如果 1 到 j 的距离比 1 到i + i到 j 的距离更大,更新距离 
				if(dis[e[i].des] > dis[e[i].sta]+e[i].val){   
					dis[e[i].des] = dis[e[i].sta]+e[i].val;
					if(!visit[e[i].des]){ // 如果当前点不在队列,入队 
						q.push(e[i].des);
						visit[e[i].des] = true;
					}
				} 
			}
		}			 
	}
}

int main(){
	Init();
	SPFA();
	for(int i=2;i<=n;i++)
		printf("%d\n",dis[i]);
	return 0;
} 

优化版本
增加了 head 数组快速查找相邻结点

#include<queue>
#include<cstdio>
#include<string.h> 
using namespace std;
const int MAXM = 200100;
const int MAXN = 21000;
const int INF = 99999999;
struct Edge{  // 边信息 
	int sta; // 起点 
	int des; // 终点
	int val;  // 权值 
	int next;  // 相同起点的下一条边编号 
}e[MAXM];
int n; // 点 
int m; // 边 
int dis[MAXN];  // 距离 
bool visit[MAXN]; // 记录点的访问状态 
int head[MAXN];  // 建立一条伪链表,head[i] 表示以点 i 为起点的边的编号 
using namespace std;

void Init(){
	scanf("%d%d",&n,&m);
	memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++){ 
		dis[i] = INF;
		visit[i] = false;
	} 
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&e[i].sta,&e[i].des,&e[i].val);
		e[i].next = head[e[i].sta];
		head[e[i].sta] = i;
	}
}

void SPFA(){
	queue<int> q;
	q.push(1);  // 第一个点入队列 
	visit[1] = true;  // 入队改变状态 
	dis[1] = 0;
	int f; // 队列第一个元素
	while(!q.empty()){
		f = q.front();
		q.pop();
		visit[f] = false;  // 已经取出来了
		for(int i=head[f];i!=-1;i=e[i].next){  // 遍历以 f 为起点的所有值 
			// i 到 j 有边
			// 如果 1 到 j 的距离比 1 到i + i到 j 的距离更大,更新距离 
			if(dis[e[i].des] > dis[f]+e[i].val){   
				dis[e[i].des] = dis[f]+e[i].val;
				if(!visit[e[i].des]){ // 如果当前点不在队列,入队 
					q.push(e[i].des);
					visit[e[i].des] = true;
				}
			}
		}		
	}
}


int main(){
	Init();
	SPFA();
	for(int i=2;i<=n;i++)
		printf("%d\n",dis[i]); 
	return 0;
} 
全部评论

相关推荐

2025-12-08 16:04
门头沟学院 Java
本人本科末9,今年大三。大一大二一直玩,什么都没学到,在大学混日子混了两年,每天不是在打农就是在steam。大三开学时一个和自己玩的好的同学去实习了,才发现自己白白浪费了两年的时间,如果真不冲一下就真去京东,阿里,美团送外卖了今年9月份开始学Java,一开始一直跟着黑马视频看,后面发现看视频效率太低了,时间根本不够,就开始主要看文档和看书了。这几个月一直在学,真的尽力了,希望暑期前能找一份好点的实习。我简历上面的项目大多没有指标,但是实际上我是真没多少时间去做项目,我基本主要是动手只做了外卖和天机,黑马点评和12306我都是只是看了项目。主要是自己的时间真的不多,但是这样子自己的代码能力确实比较差。而且自己也没有做过实际的工程,我顶多用jmeter测试一下接口tps啥的,比如使用Redis管道提升了一点性能,减少Redis交互,这种值得写上去吗?需不需要具体到某些数字求求各位佬给一些建议,看看简历怎么优化?项目介绍是不是不够详细?没有具体到业务方面。项目会不会提到大致实现原理导致面试官一看简历就知道怎么实现就没有问的欲望?专业技能一些字段是不是要加粗,是不是写太啰嗦了?有没有必要压缩内容变成一页?两页的话是不是都要把两页填地满满的。
给秋招一个交代:一页简历最好,网上做的项目放面试官眼里都是玩具,简历上不需要强调有什么难点,记住就行防止真的问。然后背八股,多投多面试就行
点赞 评论 收藏
分享
2025-12-28 16:32
重庆邮电大学 Java
程序员花海:1.技能放最后,来面试默认你都会,技能没啥用 2.实习写的看起来没啥含金量,多读读部门文档,包装下 接LLM这个没含金量 也不要用重构这种 不会给实习生做的 3.抽奖这个还是Demo项目,实际在公司里面要考虑策略,满减,触发点,触发规则 库存 之类的,不是这个项目这么简单 4.教育背景提前,格式为 教育背景 实习 项目 技能 自我评价
简历被挂麻了,求建议
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务