题目链接:Ideal Path
题目描述:
给定一张无向图,无向图的每条边有一个给定的编号,保证从 1 1 1号结点可以到达 n n n号结点,在保证从 1 1 1号结点到 n n n号结点距离最短的情况下同时保证经过路径编号的字典序尽可能的小,输出最短距离和经过边的编号。
题解:
刘汝佳在紫书中提到:首先进行一次逆序的BFS求出 d i s [ i ] dis[i] dis[i]表示i到终点的最短距离,然后接着正序做BFS,每次到达新结点时要保证 d i s dis dis减少 1 1 1(这样走一定是最短路),如果有多条边都能让dis减少 1 1 1那么则选择字典序较小的边,如果最小值存在多个,那么则需要保存所有的结点,在下一轮的时候进行考虑。
上面的方法进行了两次BFS,是可行的,但是刘汝佳指出只需要一次逆序的BFS也可以解决该问题。我们在逆序BFS的时候,记录每次经过的边与颜色,如果到达新的结点时,那么直接更新即可;当我们从某个节点 u u u,到达一个访问过的结点 v v v时,如果这两个结点的到终点的最短距离相差 1 1 1,那么从 u u u到 v v v以及之前到达 u u u的边一起也会组成一条到达 v v v的最短路径,这个时候我们需要字典序最小,则应该比较两条路径上边的编号,找到第一个不同的编号,编号小的路径的字典序会更小,此时进行更新即可。
代码:
#include <bits/stdc++.h>
const int MAXN = 100010;
const int MAXM = 200010;
using namespace std;
int n, m, ecnt;
int head[MAXN], dis[MAXN], path[MAXN], pathColor[MAXN];
struct EdgeList
{
int from;
int to;
int color;
int nex;
}es[MAXM << 1];
void addEdge(int u, int v, int color)
{
es[ecnt].to = v;
es[ecnt].color = color;
es[ecnt].nex = head[u];
head[u] = ecnt++;
}
void bfs()
{
queue<int> q;
q.push(n);
memset(dis, -1, sizeof(dis));
memset(pathColor, 0x3f, sizeof(pathColor));
dis[n] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i != -1; i = es[i].nex) {
int v = es[i].to;
int c = es[i].color;
if (dis[v] == -1) {
dis[v] = dis[u] + 1;
q.push(v);
pathColor[v] = c; //到达当前结点的边的颜色
path[v] = u; // 到达当前结点的前一个结点
} else if (dis[v] == dis[u] + 1 && c <= pathColor[v]) {
// 同时是最短路径,则需要比较之前访问的路径,找到第一个颜色不同的,保留颜色较小的路径
if (c < pathColor[v]) {
pathColor[v] = c;
path[v] = u;
continue;
}
int oldLas = path[v], nowLas = u;
while (oldLas != nowLas && pathColor[oldLas] == pathColor[nowLas]) {
oldLas = path[oldLas];
nowLas = path[nowLas];
}
if (pathColor[oldLas] > pathColor[nowLas]) { path[v] = u; } // 此处不更新pathColor[v]是因为pathColor[v]与c相等
}
}
}
cout << dis[1] << endl;
int cur = 1;
while(true) {
cout << pathColor[cur];
cur = path[cur];
if (cur == n) {
cout << endl;
break;
}
cout << " ";
}
}
int main()
{
ios::sync_with_stdio(false);
while(cin >> n >> m) {
memset(head, -1, sizeof(head));
ecnt = 0;
for (int i = 0; i < m; i++) {
int u, v, c;
cin >> u >> v >> c;
addEdge(u, v, c);
addEdge(v, u, c);
}
bfs();
}
return 0;
}