UVa1599 Ideal Path (双向BFS求最短路+打印路径)

题目链接

New labyrinth attraction is open in New Lostland amusement park. The labyrinth consists of n rooms connected by m passages. Each passage is colored into some color ci. Visitors of the labyrinth are dropped from the helicopter to the room number 1 and their goal is to get to the labyrinth exit located in the room number n. Labyrinth owners are planning to run a contest tomorrow. Several runners will be dropped to the room number 1. They will run to the room number n writing down colors of passages as they run through them. The contestant with the shortest sequence of colors is the winner of the contest. If there are several contestants with the same sequence length, the one with the ideal path is the winner. The path is the ideal path if its color sequence is the lexicographically smallest among shortest paths. Andrew is preparing for the contest. He took a helicopter tour above New Lostland and made a picture of the labyrinth. Your task is to help him find the ideal path from the room number 1 to the room number n that would allow him to win the contest. Note: A sequence (a1, a2, . . . , ak) is lexicographically smaller than a sequence (b1, b2, . . . , bk) if there exists i such that ai < bi , and aj = bj for all j < i.

Input
The input file contains several test cases, each of them as described below.
The first line of the input file contains integers n and m — the number of rooms and passages, respectively (2 ≤ n ≤ 100000, 1 ≤ m ≤ 200000). The following m lines describe passages, each passage is described with three integer numbers: ai, bi, and ci — the numbers of rooms it connects and its color (1 ≤ ai, bi ≤ n, 1 ≤ ci ≤ 109). Each passage can be passed in either direction. Two rooms can be connected with more than one passage, there can be a passage from a room to itself. It is guaranteed that it is possible to reach the room number n from the room number 1.

Output
For each test case, the output must follow the description below. The first line of the output file must contain k — the length of the shortest path from the room
number 1 to the room number n. The second line must contain k numbers — the colors of passages in the order they must be passed in the ideal path.

1.题目大意: 有n个点m条边的无向无权图,每条边都涂上一个颜色。求从1节点到n节点的一条最短路径并且走过的颜色序列字典序是最小的

2.刚看题,嗯,最短路,颜色不就是权值嘛。n有1e5,从1号节点跑一遍Dijkstra不就行了嘛,至于路径,记录1到每个节点的颜色权值和d数组,那么路径上的后继减去前驱就是该路的颜色。然后就这样交了一发,TLE…

3.仔细想想,题目说的颜色并不是权值,Dijkstra是贪心算法,只能确定最短路径的权值和最小,并不能保证走的边数是最少的,例如下面这个例子,加入我们按Dijkstra来看肯定是走1-3-4-5这条路,但是这显然不是无权图的最短路,而且得到的颜色序列111字典序也大于13

在这里插入图片描述
4.看了几遍刘汝佳的思路,确实清晰了很多,无权图的最短路本来就是BFS直接求的,但是因为又要求最小颜色序列,直接跑一次BFS并不会得到正确结果。那么,先从终点倒着BFS一遍得到每个节点到终点的路径长度d[i],然后再从起点u开始,保证每到达一个节点v都有d[u]-d[v]=1,那么就是走的最短路。此外,可能有多种走法,那么选出颜色最小的,并将多个颜色最小的后面的节点入队,下一次考虑当前队列中所有的颜色值,这样再经过一遍BFS,就能得到结果

5.其实本题也可以进行一遍BFS,我觉得就是反向BFS时既保证走最短路又保证颜色序列最小,显然一个BFS就能得到答案(笔者没有尝试去写)

6.邻接表存图,链式前向星更好

代码:

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int,int> P;
const int maxn=1e5+10;
const int maxm=2e5+10;
struct edge{
    int to,col;
    edge(int b,int c){ to=b; col=c; }
};
int n,m;
vector<edge> G[maxn]; //邻接表
int d[maxn];
bool vis[maxn];  //两次bfs都用,注意初始化
vector<int> ans;  //存最小颜色序列

void init(){
    for(int i=1;i<=n;i++) G[i].clear();
    ans.clear();
    memset(d,0,sizeof d);
}

void re_bfs(){  //反向bfs
    memset(vis,0,sizeof vis);
    queue<int> q;
    q.push(n);
    d[n]=0;
    vis[n]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i].to;
            if(!vis[v]){
                vis[v]=1;
                d[v]=d[u]+1; //只走一条,显然从后往前每次直接加一即可
                q.push(v);
            }
        }
    }
}

void bfs(){
    memset(vis,0,sizeof vis);
    queue<int> q;
    q.push(1);
    vis[1]=1;
    while(!q.empty()){
        vector<int> res; //队列中有多个上一个颜色序列均是最小的,那么就都要取出考虑下一个颜色
        while(!q.empty()){ //每次清空队列,如果后面还有,队列中还会入值
            res.push_back(q.front());
            q.pop();
        }
        int Min=INF; //记录当前最小的颜色
        for(int i=0;i<res.size();i++){
            int u=res[i];
            for(int i=0;i<G[u].size();i++){
                int v=G[u][i].to;
                if(!vis[v] && d[u]==d[v]+1) Min=min(G[u][i].col,Min); //更新Min
            }
        }
        if(Min!=INF) ans.push_back(Min); //加个特判,输出样例时莫名其妙多了一个INF
        for(int i=0;i<res.size();i++){
            int u=res[i];
            for(int i=0;i<G[u].size();i++){
                int v=G[u][i].to;
                if(!vis[v] && d[u]==d[v]+1 && G[u][i].col == Min){ //多个符合条件的都要入队
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}

int main(){
    int a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        while(m--){
            scanf("%d%d%d",&a,&b,&c);
            if(a!=b){ //考虑到可能有自环
                G[a].push_back(edge(b,c));
                G[b].push_back(edge(a,c));
            }
        }
        re_bfs();
        bfs();
        printf("%d\n",d[1]-d[n]);  //d[1]-d[n]就是最短路径长度
        for(int i=0;i<ans.size();i++)
            printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值