【题意】
给一个n个点m条边(2<=n<=100000,1<=m<=200000)的无向图,每条边上都涂有一种颜色。求从结点
1到结点n的最短路径,在此前提下,经过边的颜色序列的字典序最小。一对结点间可能有多条边,一条边
可能连接两个相同结点。输入保证结点1可以到达结点n。颜色为1~10^9的整数。
【分析】
1. 本题是不错的bfs题,因为数据太大,如果直接找绝对超时。
2. 题目给的n小于等于100000,所以不能用邻接矩阵来保存,只能通过邻接表,用邻接表保存还方便进
行遍历,用结构体保存邻接表,同时记录颜色的数值。
3. 题目中无向图可能有自环和重边,自环可以在输入的时候省略掉,重边因为在输入的时候无法判断是
否最短,所以保存下来,再用此点时,再进行遍历选取最短。
【思路】
1. 先倒着bfs,得到每一个结点i到终点的最少步数d[i],然后再从起点出发,每次到达一个新结点时保
证d值恰好减少1,直到到达终点。
2. 倒着bfs,保存每一个结点i到终点的最小步数,就相当于一个层次图,将于每个离终点相同的点放在
一个层次里,再从起点开始,一层一层的走,避免了1个点多次用到。
3. 用queue和vector来进行层次图的路径的操作,用queue保存层次的点,vector保存本层次的对应的结
构体的坐标,中间有太多细节需要考虑。每走一点清一次queue,每走一层清空一次vector。标记数组记录
某点是否走过,避免重走。
4. 因为事先求出了每点i到终点的最短路径d[i],所以不用保存路径,只需求出当前步中最小的颜色的数
值,就是最优的解,直接输出就行。
【代码】
#include<stdio.h> #include<string.h> #include<vector> #include<queue> #include<iostream> using namespace std; const int MAXN=100000+5; const int MAXM=200000+5; const int INF=0x3f3f3f3f; struct node { int x; int color; int next; } G[MAXM*2]; int head[MAXN],d[MAXN]; bool book[MAXN]; int n,m,cnt; void Addedge(int a,int b,int c) { G[cnt].x=b; G[cnt].color=c; G[cnt].next=head[a]; head[a]=cnt++; } void Bfs1() { queue<int>p; d[n]=0; p.push(n); while(!p.empty()) { int x=p.front(); p.pop(); if(x==1) { printf("%d\n",d[x]); return ; } for(int i=head[x]; i!=-1; i=G[i].next) { int k=G[i].x; if(d[k]!=-1) continue; p.push(k); d[k]=d[x]+1; } } return ; } void Bfs2() { queue<int>p; // 保存下一步的点 vector<int>Vec; // 保存结构体的下坐标 p.push(1); book[1]=true; int color=INF; while(!p.empty()||Vec.size()) { if(p.empty()) { int len=Vec.size(); for(int i=0; i<len; i++) { int k=Vec[i]; int x=G[k].x; if(G[k].color==color&&!book[x]) { if(x==n) { printf("%d\n",color); return ; } p.push(x); book[x]=true; } } printf("%d ",color); Vec.clear(); color=INF; } int k=p.front(); p.pop(); for(int i=head[k]; i!=-1; i=G[i].next) { int x=G[i].x; if(d[k]-d[x]==1&&G[i].color<=color) { Vec.push_back(i); color=G[i].color; } } } return ; } int main() { while(~scanf("%d %d",&n,&m)) { memset(head,-1,sizeof(head)); memset(d,-1,sizeof(d)); memset(book,false,sizeof(book)); cnt=0; int a,b,c; for(int i=0; i<m; i++) { scanf("%d %d %d",&a,&b,&c); Addedge(a,b,c); Addedge(b,a,c); } Bfs1(); Bfs2(); } return 0; }
【收获】
本题是到不错的bfs题,考了很多内容,queue、vector、邻接表、双向bfs,使一个大数据的题,得到很好 的解决。
尤其是倒着bfs构成一个层次图,直接能求出最短路径的长度,还为下面走的路径指明了方向,节约了代码 的时间复杂度和提供了准确的路径。