- by https://blog.csdn.net/vocaloid01/article/details/81634314
- 题意
- N个点,M条无向边,每条边都有编号,经过相同的编号的边不会改变花费,经过不同的编号的边花费加一。
- 题解
- 一看就是将花费变成路径的权值的最短路类问题,刚开始想跑SPFA或Dijkstra,但边的关系处理起来比较麻烦而且耗时更多,所以就想是否可以用经典的BFS求最短路径。毕竟BFS的话每次到一个点时我们是知道当前路径与这个点的直接连边的占领者的,而SPFA过程中是不知道的。
ERROR:具体过程其实与普通的BFS求最短路也没啥区别,就是需要一个数组(dis)来记录起点(1)到所有点的最短距离,然后每个点可以多次加入queue(用优先队列)。(这种方法已发现是有BUG的)
感谢@jianbagengmu给出样例
5 6
1 2 1
2 3 1
3 4 1
4 5 1
1 3 2
1 4 3
这种方法当遇到多条线路到达同一个点的花费相同但线路占领者不同时就会出问题。不过由于出题人可能没有考虑这种情况,导致这种错误方法也能AC。(事实上多校时我就是用这种方法过的。。。_(:з」∠)_) - 正确方法:
bfs查询路径,每条边只走一次。每一个点入队列后,再用dfs把所有与当前边相同占领者的边的点加入。本质上是用dfs来压缩路径,然后bfs。因为按照普通的bfs找最短路径经验我们容易发现两个典型的性质
1:每次以当前点为中心加进来的相邻点的距离都为当前点距离+1。
2:必定为最短距离。
-
- 也就是“层层递进”,如图分成3层。因为从第1层到第2层任一点必定存在距离为1的路,第2层到第3层也是,所以我们甚至可以将3个层映射到直线上三个点如:1->2->3。那么从1到3的最短距离显而易见。
- 到此通过分析了BFS求最短距离之后我们发现只需要在这道题中分出“层”就能按BFS老套路求解。很容易想到被相同占领者的边连起来的联通块应该当作一个“格子”(通过dfs),而距离起点距离相同的“格子”自然就是一个“层”。
正确代码:
#include <bits/stdc++.h> using namespace std; const int MAXN = 100005; const int INF = 0x3f3f3f3f; int N,M; struct Edge{ int to,id,next;//id是边的占领者 bool vis; }E[MAXN*4]; int head[MAXN],tot; queue<int> Q; inline void Add(int from,int to,int id){ E[++tot].next = head[from]; E[tot].to = to; E[tot].id = id; E[tot].vis = false; head[from] = tot; E[++tot].next = head[to]; E[tot].id = id; E[tot].to = from; E[tot].vis = false; head[to] = tot; } inline void init(){ tot = 0; memset(head,0,sizeof head); } int dis[MAXN];//记录起点到所有点的最短距离 bool vis[MAXN]; void DFS(int x,int id,int num){ if(!vis[x]){ dis[x] = num; vis[x] = true; if(x == N)return; Q.push(x); } for(int i=head[x] ; i ; i=E[i].next){ if(E[i].vis)continue; if(E[i].id == id){ E[i].vis = true; DFS(E[i].to,id,num); } } } void BFS(int x){ while(!Q.empty())Q.pop(); memset(vis,false,sizeof vis); dis[x] = 0; dis[N] = -1; vis[x] = true; Q.push(x); while(!Q.empty()){ int t = Q.front(); Q.pop(); for(int i=head[t] ; i ; i=E[i].next){ if(E[i].vis)continue; E[i].vis = true; DFS(E[i].to,E[i].id,dis[t]+1); if(dis[N] > 0)break; } if(dis[N] > 0)break; } } int main(){ while(scanf("%d %d",&N,&M) == 2){ init(); if(M == 0){ printf("-1\n"); continue; } int a,b,c; for(int i=1 ; i<=M ; ++i){ scanf("%d %d %d",&a,&b,&c); Add(a,b,c); } BFS(1); printf("%d\n",dis[N]); } return 0; }
- 一看就是将花费变成路径的权值的最短路类问题,刚开始想跑SPFA或Dijkstra,但边的关系处理起来比较麻烦而且耗时更多,所以就想是否可以用经典的BFS求最短路径。毕竟BFS的话每次到一个点时我们是知道当前路径与这个点的直接连边的占领者的,而SPFA过程中是不知道的。