题目描述
给定一个 n n 个点条边( 2≤n≤100000,1≤m≤20000 2 ≤ n ≤ 100000 , 1 ≤ m ≤ 20000 )的无向图。每一条边上都有一种颜色。求从 1 1 号节点到号节点的最短路径,当路径大小相同时,取所经过节点的颜色序列字典序最小的一条路径。一对节点间可以有多天路径,同时存在自反路径。
题解
该题目中若无取字典序最小的路径的要求,则是一个简单的求最短路径问题。当然,该题中的节点个数较多。不能使用矩阵存储信息,应该使用广义表。同时,由于节点个数多,不能使用保存前驱节点的方式求最短路径。但可以反向bfs,求出每一个节点 i i 到终点的最短距离,便可求出最短路径。其模板总结如下:
反向bfs求最短路径
void bfs(int n) {
queue<int> qu;
qu.push(n);// n 为终点
d[n] = 1; // 将终点的距离初始化为1.不初始化为0是为了区分已访问的点和未访问的点。
while(!qu.empty()){
int s = qu.front();qu.pop();
for(int i = 0; i<n; i++) if(!d[G[s][i].n]){
d[i] = d[s]+1;
if(s == 1) return;// 最终路径长度为d[1] - 1;
qu.push(G[s][i].n);
}
}
}
这样就可以解决求最短路径问题。然而还有另一个问题:取颜色字典序最短的路径。解决方法是,再正向进行一遍bfs。这次关注的重点是节点间的“颜色”。进入队列的元素是下一层中“颜色”最小的节点,若都最小则全部入队。在实现过程中,控制入队节点的个数很重要。不然很容易超时。尽量先判断能否继续再入队,同时要防止重复入队。这里可以附加一个是否入队的判断。
综合考虑就可以解决问题。
AC代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 100001;
const int INF = 0x3f3f3f3f;
struct link{
int n, c;
link(int n, int c):n(n), c(c){}
};
vector<link> G[maxn];
int d[maxn];
bool inqueue[maxn];
void panswer(int n){
queue<int> ans;
ans.push(1);
int out[maxn], layer;
memset(out, INF, sizeof(out));
while(!ans.empty()){
int s = ans.front();ans.pop();
if(s == n) break;
int color = INF, len = G[s].size();
for(int i = 0; i<len; i++)if(d[s] == d[G[s][i].n]+1) color = min(color, G[s][i].c);
for(int i = 0; i<len; i++)if(d[s] == d[G[s][i].n]+1 && !inqueue[G[s][i].n] && color == G[s][i].c){
ans.push(G[s][i].n); inqueue[G[s][i].n] = true;
}
layer = d[1] - d[s];
out[layer] = min(out[layer], color);
}
printf("%d\n%d", d[1] - 1, out[0]);
for(int i = 1; i<d[1]-1; i++) printf(" %d", out[i]);
printf("\n");
}
void solve(int n) {
queue<int> qu;
qu.push(n);
d[n] = 1;
while(!qu.empty()){
int s = qu.front();qu.pop();
for(int i = 0, k = G[s].size(); i<k; i++)if(!d[G[s][i].n]){
d[G[s][i].n] = d[s]+1;
if(s == 1) return;
qu.push(G[s][i].n);
}
}
}
void clearG(int n){ for(int i = 1; i<=n+1; i++) G[i].clear(); }
int main(){
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int n,m;
while(scanf("%d%d", &n, &m)!=EOF){
clearG(n);
memset(d, 0, sizeof(d));
int a, b, c;
for(int i = 0; i<m; i++){
scanf("%d%d%d", &a, &b, &c);
if(a != b){
G[a].push_back(link(b, c));
G[b].push_back(link(a, c));
}
}
solve(n);
memset(inqueue, false, sizeof(inqueue));
panswer(n);
}
return 0;
}