原题
最近课业比较繁忙, 好久没时间写题了。
这也算是最短路径问题的改版,求满足一个条件的最短路径。
经验不足,一开始自然就想到用DFS(因为比较好写),结果当然TLE了
后来才想起来DFS的复杂度是O(N^2), 而BFS是O(N+E),
所以遇到这种规模比较大的图肯定要用BFS了, 根本不是一个数量级啊
网上虽然方法比较多, 但是描述得不是很全面, 我再来整理一下吧
主要思路是用两次BFS, 比较巧妙
第一次逆向BFS(从终点出发)找出终点到所有点的距离并标记, 比较简单就不赘述了
第二次BFS是关键, 从起点出发, 首先要知道下一次走的点应该是那些与终点距离只比起点少1的点, 只有这样才能保证走的都是最短路径.
每到一个点, 关键就是用一个点集nextVertices来存储下一步能走的所有点之中边权最小的点(而不是单独的一个变量表示当前的点, 就相当于多路同步进行),
我是先遍历一边所有可能走的边找出最小边权MinColor, 然后再遍历以便找出边权等于MinColor的点, 就放进下一轮要选择走的点集nextVertices内
这里需要注意的一个小细节, 刚要开始第二次BFS的时候, 只把起点放入nextVertices就能开始上述循环了
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <list>
#include <cassert>
#include <iomanip>
using namespace std;
const int MAXN = 200000+1;
const int INF = 1000000000 + 10;
typedef long long LL;
/*
uva 1599
关键 : 0. 用BFS而不用DFS的原因是在结点较多的情况下BFS(O(V+E))更快
1. 第一次倒着BFS找到终点到其余各点的最短路径长度,
[关键难点]第二次正向BFS从中找到到达终点的最短路径
2. 第二次如果有color值相同的边如何确定下一个点?
解决方法 : 先从与当前点邻接的边中找出所有边最小的color值 min_color,
之后再遍历容器内的边, 找出color值等于min_color的所有边,
保存这些边指向的顶点, 这些顶点即为下一轮需要BFS的顶点
*/
map<int, map<int,int> > G; // 无向网图
int M, N;
bool isVisited[MAXN];
int Distance[MAXN]; // 记录各个点到终点的距离
typedef map<int,int>::iterator MPIT;
//
bool Lessthan(int * path1, int * path2){
while( *path1++ == *path2++ );
return *path1 < *path2;
}
void BFS_first(){
queue<int> Q;
Q.push(N);
int dis = 0;
Distance[N] = 0;
while( !Q.empty() ){
int cur = Q.front(); Q.pop();
MPIT it = G[cur].begin();
while( it!=G[cur].end() ){
if( !Distance[it->first] && it->first!=N ){
Distance[it->first] = Distance[cur]+1;
Q.push(it->first);
}
it++;
}
}
// for(int i=1; i<=N; i++) std::cout << Distance[i] << " " << endl;
}
void BFS_second(){
vector<int> ans, nextVertices;
MPIT it;
int cur;
memset(isVisited, 0, sizeof(isVisited));
nextVertices.push_back(1);
isVisited[1] = true;
for(int i=0; i<Distance[1];i++ ){
int min_color = INF;
for(int j=0; j<nextVertices.size(); j++){
cur = nextVertices[j];
it = G[cur].begin();
while( it!=G[cur].end() ){
if(Distance[cur]-1 == Distance[it->first]
&& G[cur][it->first] < min_color ){
min_color = G[cur][it->first];
}
it++;
}
}
ans.push_back(min_color);
vector<int> tmpNext;
for(int i=0; i<nextVertices.size(); i++){
cur = nextVertices[i];
it = G[cur].begin();
while( it!=G[cur].end() ){
if( !isVisited[it->first] && Distance[cur]-1 == Distance[it->first]
&& G[cur][it->first] == min_color ){
isVisited[it->first] = true;
min_color = G[cur][it->first];
tmpNext.push_back(it->first);
}
it++;
}
}
nextVertices = tmpNext;
}
cout << ans.size() << endl;
for(int i=0; i<ans.size()-1; i++) cout << ans[i] << " ";
cout << ans[ans.size()-1] << endl;
}
int main(){
// freopen("input2.txt","r",stdin);
while( cin >> N >> M ){
int a, b, c;
G.clear();
memset(Distance, 0, sizeof(Distance));
for(int i=0; i<M; i++){
cin >> a >> b >> c;
if( G[a][b]==0 || c < G[a][b] ){ // 当没有加过边或者准备要加的边更短就更新边
G[a][b] = G[b][a] = c;
}
}
BFS_first();
BFS_second();
}
return 0;
}