#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 101000;
const int inx = 1 << 30;
bool vis[maxn];
int d[maxn],n,m;//存储最短路径
vector<int>g[maxn];
struct edge
{
int u, v, c;
edge(int u=0,int v=0,int c=0):u(u),v(v),c(c){}//存储路径,注意颜色作为权值储存
};
vector<edge>map;
void build(int u,int v,int c)
{
map.push_back(edge(u, v, c));//map数组存储所有连通的情况
int record = (int)map.size() - 1;
g[u].push_back(record);//记录有几条边存在连通关系
}
void re_bfs()//反向bfs,根据路径寻找符合最短路径的情况
{
memset(vis, 0, sizeof(vis));
d[n - 1] = 0;
vis[n - 1] = true;
queue<int>q;
q.push(n - 1);
while(!q.empty())
{
int u = q.front(); q.pop();
for (int i = 0; i < g[u].size(); i++)
{
int count = g[u][i];
int v= map[count].v;//从终点开始bfs寻找下一个节点,计算路径
if (!vis[v])
{
vis[v] = true;
d[v] = d[u] + 1;//永远保证所求路径为最短路径
q.push(v);//下一个节点进入判断寻找
}
}
}
}
vector<int>ans;
void bfs()
{
memset(vis, 0, sizeof(vis));
ans.clear();
vis[0] = true;
vector<int>next;
next.push_back(0);//从起点开始寻找,注意取点时的控制
for (int i = 0; i < d[0]; i++)//d[0]代表求出的最短路径
{
int min_color = inx;//寻找最小的颜色,及所有满足条件的点中最小的权值
for (int j= 0; j < next.size(); j++)
{
int u = next[j];//取出当前的点进行判断
for (int k = 0; k < g[u].size(); k++)//与当前点存在连通关系的点
{
int e = g[u][k];//g[u][k]用于取出map中的元素
int v = map[e].v;
if (d[u] == d[v] + 1)//满足路径关系,注意逆向bfs时记录的最短路径,d[0]为最短路径,从起点开始,路径依次减一
{
min_color = min(min_color, map[e].c);//每层的路径寻找都寻找最小的权值,满足权值关系之后再进行下一层的寻找
}
}
}
ans.push_back(min_color);
vector<int>next2;//满足条件的存在连通关系的点
for (int j = 0; j < next.size(); j++)
{
int u = next[j];//该层所有满足的点都寻找下一层的节点
for (int k = 0; k < g[u].size(); k++)
{
int e = g[u][k];
int v = map[e].v;
if (d[u] == d[v] + 1 && !vis[v] && map[e].c == min_color)//可能存在多个满足颜色条件的节点,细节问题注意前后路径关系
{
vis[v] = true;//控制访问条件,避免可能出现的重复访问的情况
next2.push_back(v);
}
}
}
next = next2;
}
printf("%d\n", (int)ans.size());//最终输出的个数即为最终的最短路径
for (int i = 0; i < ans.size(); i++)
{
if (i != ans.size() - 1)
printf("%d ", ans[i]);
else
printf("%d\n", ans[i]);
}
}
int main()
{
int u, v, c;
while (scanf("%d %d", &n, &m) == 2)
{
for (int i = 0; i < n; i++)
g[i].clear();
while (m--)
{
scanf("%d%d%d", &u, &v, &c);
build(u - 1, v - 1, c);
build(v - 1, u - 1, c);
}
re_bfs();//反向bfs寻找满足条件的路径
bfs();
}
getchar();
getchar();
return 0;
}
1、首先理解几个数组设立的作用,g数组用于记录map中存储的位置关系的数组,存储位置关系时,注意所有连通关系一起处理,g数组则是用于记录节点为u时,存在几个对应关系,在map数组里处在什么位置
2、本题重要思想,反向bfs方法,注意此处理解,因为本题求解的最短路径,因为正向bfs寻找最小权值的点,无法保证得到的路径为最短路径,反向bfs则是始终保证最终d[0]所求的路径为最短路径,正向bfs时则每移动一步,步数减一
3、正向bfs时,注意权值大小比较的控制,当满足最短路径要求时,将所有的点进行颜色的比较,满足两者要求才将相应节点放入数组重新bfs寻找
4、易错点注意路径的控制,正向bfs时,路径应该是前一节点比后一节点大一,路径依次减一,注意路径控制的细节,想清楚
5、节点存储时,注意正反存储,图论问题解决时注意节点之间关系的存储
6、此题还有一巧妙之处,将字典序输出转换为颜色权值的比较,每个层次的节点依次输出权值最小的点,保证最后结果的字典序最小
7、图论中最短路径的问题,注意反向bfs用于确定最短路径的思路,重点理解反向bfs的思想