uva 1599

原题
最近课业比较繁忙, 好久没时间写题了。
这也算是最短路径问题的改版,求满足一个条件的最短路径。
经验不足,一开始自然就想到用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;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值