【分析】
本题只是一个普通的最短路问题,可以用BFS解决。但是“记录父结点”的方法已经不适用了,因为这样打印出来的路径并不能保证字典序最小。
事实上。无须记录父结点也能得到最短路,方法是从终点开始“倒着”BFS,得到每个结点i到终点的最短距离d[i],然后直接从起点开始走,但是每次到达一个新结点时要保证d值恰好减少1(如有个选择则可以随便走),直到到达终点。
直接从起点开始按照上述规则走,如果有多种走法,选颜色字典序最小的走;如果有多条边的颜色字典序都是最小,则记录所有这些边的终点,走下一步时要考虑从所有这些点出发的边。这实际上是又做了一次BFS,因此时间复杂度仍为O(m)。
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll inf = 1e9 + 5;
const int maxn = 100000 + 5;
int n, m, d[maxn], vis[maxn], nextNode[maxn], tempNode[maxn];
struct edge
{
int v, w;
edge(int b, int c) : v(b), w(c) {}
};
vector<edge> e[maxn];
int main(void)
{
while (~scanf("%d %d", &n, &m))
{
memset(vis, 0, sizeof(vis));
memset(d, inf, sizeof(d));
for (int i = 1; i <= n; i++)
e[i].clear();
while (m--)
{
int a, b;
ll c;
scanf("%d %d %lld", &a, &b, &c);
e[a].push_back(edge(b, c));
e[b].push_back(edge(a, c));
}
queue<int> Q;
Q.push(n);
vis[n] = 1;
d[n] = 0;
while (!Q.empty())
{
int u = Q.front();
Q.pop();
for (int i = 0; i < e[u].size(); i++)
{
int v = e[u][i].v;
if (vis[v])
continue;
vis[v] = 1;
d[v] = d[u] + 1;
Q.push(v);
}
}
memset(vis, 0, sizeof(vis));
vis[1] = 1;
int size = 0;
nextNode[size++] = 1;
printf("%d\n", d[1]);
for (int step = 0; step < d[1]; step++)
{
int cnt = 0;
ll minColor = inf;
for (int i = 0; i < size; i++)
{
int u = nextNode[i]; //所有的u和n的距离都相同
for (int j = 0; j < e[u].size(); j++)
{
int v = e[u][j].v;
if (d[v] != d[u] - 1)
continue; //保证再走一步会缩小距离
if (e[u][j].w <= minColor)
{ //找最小的颜色
if (e[u][j].w < minColor)
{
cnt = 0;
minColor = e[u][j].w;
}
tempNode[cnt++] = v;
}
}
}
printf("%lld%c", minColor, (step == d[1] - 1) ? '\n' : ' ');
int cnt2 = 0;
for (int i = 0; i < cnt; i++)
{
if (vis[tempNode[i]])
continue;
vis[tempNode[i]] = 1;
nextNode[cnt2++] = tempNode[i];
}
size = cnt2;
}
}
system("pause");
return 0;
}