目录
题目大意
给一个有n个点和m条边的无向图(2≤n≤100000,1≤m≤200000)的无向图,每条边上涂有一种颜色c(1≤c≤1e9),求从结点1到结点n的一条最短路径,若有多条最短路径,选择其中颜色序列字典序最小的一条。打印最短路径长度和经过的颜色序列。原题链接
例:4 6 => 2
1 2 1 1 3
1 3 2
3 4 3
2 3 1
2 4 4
3 1 1
理论基础
无向图:图中任意两个顶点之间的边都是无向的(也称双向)。
队列(Queue):核心是先进先出(First-In-First-Out)的线性表,允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)。顾名思义类似排队,只能从队尾入队,从队头出队
大家都不插队。示意图如下。
队列 BFS:即广度优先搜索算法(Breadth-First-Search),是一种图/树搜索算法。基本思想是从源点(起点)开始,沿着相连边分层遍历图的点,若到达目标结点(简单BFS),则可以停止搜索。与DFS最大区别是BFS不需要回溯。示意图如下,稍微观察就会发现其结点的遍历顺序使其可以很方便地借助队列实现。
BFS示意图
解题思路
这题相当于最短路径外加字典序判断,可用BFS解决。注意:Dijkstra 算法解决的是带权最短路径,而这里是无权最短路径。
刚开始写成Dijkstra了嘎嘎超时。简单的BFS只能记录最短距离,为了记录具体路径可以另开一个数组存储当前结点的父结点和经过的颜色。为了保证颜色序列字典序最小,每个结点第一次遇到路径的应是字典序最小的,可以先将结点的所有边按颜色从小到大排序后再对颜色相同的边按层进行遍历。
简单的BFS(一个一个结点进行遍历)无法区分队列中的结点来自哪一层。我们可以向队列添加个标志(取到标志代表当前层已结束),再利用数组将当前层所有边取出,来达到按层遍历的效果。
参考代码
#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
struct edge {
int x, y, c;
bool operator<(const edge& b)const {//重载小于号,sort排序使用
return c < b.c;
}
} atemp, btemp;
int steps[maxn];//记录源点到各顶点的最短路长度
vector<edge> edges[maxn], v;//edges存放边,相当于图按邻接表存储
vector<pair<int, int>> road(maxn * 2);//记录父结点和经过边的颜色
vector<int> result;//存放最终颜色序列
int main() {
int n, m;
while (scanf("%d%d", &n, &m) != EOF) {
queue<int> q;
memset(steps, 0x3f, maxn * 4);//最短路置无穷
steps[1] = 0;//源点到自身的距离为0
road.clear();//清空
result.clear();
for (int i = 1; i < maxn; i++)
edges[i].clear();
for (int i = 0; i < m; ++i) {
int x, y, c;
scanf("%d%d%d", &x, &y, &c);
if (x != y) {//存储无向图,忽略首尾相同的边
atemp.x = btemp.y = x;
atemp.y = btemp.x = y;
atemp.c = btemp.c = c;
edges[x].push_back(atemp);
edges[y].push_back(btemp);
}
}
q.push(1);/*从源点开始bfs。其实可以从终点进行逆向BFS
这样得到的颜色序列就是正序,无需倒置。或2次BFS(一反一正)解决*/
q.push(0);//0作为层的结束标记
while (q.size()) {
v.clear();
while (1) {//取出此层的全部边
int node = q.front();
q.pop();
if (!node)break;
for (int i = 0; i < edges[node].size(); ++i)
v.push_back(edges[node][i]);
}
sort(v.begin(), v.end());//对边按颜色字典序排序
for (int i = 0, j; i < v.size(); i = j) {
for (j = i + 1; j < v.size() && v[i].c == v[j].c; ++j)
;//查找颜色相同的边
for (int k = i; k < j; ++k)//同时更新相同颜色的边的邻接点
if (steps[v[k].y] > steps[v[k].x] + 1) {
steps[v[k].y] = steps[v[k].x] + 1;
q.push(v[k].y);
road[v[k].y] = {v[k].x, v[k].c};
}
q.push(0);
}
}
printf("%d\n", steps[n]);
for (pair<int, int> i = road[n]; i.first; i = road[i.first])
result.push_back(i.second);//沿着终点回溯到源点,颜色序列为倒序
reverse(result.begin(), result.end());//倒置颜色
for (int i = 0; i < result.size(); i++)
printf("%d%c", result[i], " \n"[i == result.size() - 1]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】