【题目链接】:UVA - 1599
【题意】:
输入n,m表示有n个房间,m条道路连接房间。再输入m组u,v,c,分别表示连接的房间和路的颜色,求从房间1 - n,走过路的颜色数最少且字典序最小。输出颜色数,和对应的颜色。
紫书的题,书中注明此题非常重要。
【思路】:
先倒着进行bfs,保存下每个点到终点的最短距离。在正着进行bfs,算出字典序最小的道路。
【代码】:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 1e5 + 10;
const int INF = 1e9;
struct Edge{
int u,v,c;
Edge(int u=0,int v=0,int c=0) : u(u),v(v),c(c) {}
};
vector<Edge> edges;
vector<int> G[MAXN];
void AddEdge(int u,int v,int c){
edges.push_back(Edge(u,v,c));
int idx = edges.size() - 1; //记录u所对应v在G[u]中的位置
G[u].push_back(idx);
}
int n,vis[MAXN];
int d[MAXN]; //记录每个节点到终点的距离
//倒着进行bfs
void rev_bfs(){
memset(vis,0,sizeof(vis));
d[n - 1] = 0;
vis[n - 1] = 1;
queue<int> q;
q.push(n - 1);
while(!q.empty()){
int v = q.front();
q.pop();
for(int i=0;i<G[v].size();i++){
int e = G[v][i];
int u = edges[e].v;
if(!vis[u]){
vis[u] = 1;
d[u] = d[v] + 1;
q.push(u);
}
}
}
}
vector<int> ans;
//再正着进行一遍bfs找到结果
void bfs(){
memset(vis,0,sizeof(vis));
vis[0] = 1;
ans.clear();
vector<int> next;
next.push_back(0);
for(int i=0;i<d[0];i++){
int min_color = INF;
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 = edges[e].v;
if(d[u] == d[v] + 1)
min_color = min(min_color,edges[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 = edges[e].v;
if(d[u] == d[v] + 1 && !vis[v] && edges[e].c == min_color){
vis[v] = 1;
next2.push_back(v);
}
}
}
next = next2;
}
printf("%d\n",ans.size());
printf("%d",ans[0]);
for(int i=1;i<ans.size();i++)
printf(" %d",ans[i]);
printf("\n");
}
int main(){
int m,u,v,c;
while(scanf("%d%d",&n,&m) == 2){
edges.size();
for(int i=0;i<n;i++)
G[i].clear();
for(int i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&c);
AddEdge(u-1,v-1,c);
AddEdge(v-1,u-1,c);
}
rev_bfs();
bfs();
}
return 0;
}