题意:n个点m条边,可能有自环和重边的无向图,每条边上有一个数,求点1到点n字典序最小的最短路。多组数据。 从JY小朋友那里得知这道有趣的题目。
WC营员交流虽然基本没听懂什么......但学来一点,遇到一般图考虑一下生成树,尤其是DFS树和BFS树。
不带权的图的BFS树和最短路径树是一个东西。BFS树有一个特点:每一层的非树边只能连向同一层、上一层、下一层,否则就不是最短路径了。以点n为根建一棵BFS树,再从点1向根跑,只向上,就能获取点1到点n的所有最短路径,而且分好了层。贪心,每层只扩展字典序最小的边即可。
扩展的时候没对点去重,TLE一发。QAQ
把排序换成布尔数组,可以得到线性算法。不是通常那样扩展下一层时判重,而是本层的点不扩展两次,类似于优先队列实现的Dijkstra。
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int MAX_N = 1e5, MAX_M = 2e5, inf = (1<<30)-1;
struct Edge {
int v, c;
bool operator<(const Edge& rhs) const
{
return c < rhs.c;
}
};
vector<Edge> adj[MAX_N+1];
int n, d[2][MAX_N+1], path[MAX_N];
void bfs(int s, int d[])
{
queue<int> Q;
Q.push(s);
fill_n(d+1, n, inf);
d[s] = 0;
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = 0; i < adj[u].size(); ++i) {
int v = adj[u][i].v;
if (d[v] == inf) {
d[v] = d[u] + 1;
Q.push(v);
}
}
}
}
void build(int dis)
{
static Edge e[MAX_M];
static int q[MAX_N];
q[0] = 1;
int top = 1;
for (int i = 0; i < dis; ++i) {
int m = 0;
while (top) {
int u = q[--top];
for (int k = 0; k < adj[u].size(); ++k) {
int v = adj[u][k].v;
if (d[0][v] == d[0][u]+1 && d[1][u] == d[1][v]+1)
e[m++] = adj[u][k];
}
}
int mn = inf;
for (int j = 0; j < m; ++j)
mn = min(mn, e[j].c);
path[i] = mn;
for (int j = 0; j < m; ++j)
if (e[j].c == mn)
q[top++] = e[j].v;
sort(q, q+top);
top = unique(q, q+top) - q;
}
}
int main()
{
int m;
while (scanf("%d %d", &n, &m) == 2) {
while (m--) {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
adj[a].push_back((Edge){b, c});
adj[b].push_back((Edge){a, c});
}
bfs(1, d[0]);
bfs(n, d[1]);
int dis = d[0][n];
build(dis);
printf("%d\n", dis);
for (int i = 0; i < dis; ++i)
printf("%d%c", path[i], " \n"[i == dis-1]);
for (int i = 1; i <= n; ++i)
adj[i].clear();
}
return 0;
}