zoukankan      html  css  js  c++  java
  • 浙大PAT CCCC L3-014 周游世界 ( 最短路变形 )

    题目链接

    题意 : 中文题请点链接,挺复杂的...

    分析 : 乍一看是个最短路,实际就真的是个最短路。如果没有 “ 在有多条最短路径的时候输出换乘次数最少的” 这一条件的约束,那么这题就是直接建图然后跑个 Dij 就行了,那有了这个约束条件还是要大胆的向最短路思路靠,题目既然需要换乘次数少的,那么我们在进行最短路松弛操作的时候,面对松弛过后最短路径相等的情况就要分开讨论,这时候为了方法取最优值,需要多记录一个信息 ==> 跑到当前点时候换乘次数是多少次,开个数组来记录就行了,其他的还是按最短路来跑。这题就是编码烦了点,不对!是非常烦_(:3 」∠)_ 

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e4 + 10;
    struct EDGE{ int v, nxt; }Edge[maxn<<2]; ///向前星存图
    struct NODE{ ///跑DIJ时塞在优先队列的结构体
        int v, Pre_v, TransferCnt, Dist; ///当前是哪个点、其前一个点是什么、换乘次数、源点到此点的最短距离
    
        NODE(int V, int D, int Pv, int TCnt):v(V),Dist(D),Pre_v(Pv),TransferCnt(TCnt){};
        bool operator < (const NODE &rhs) const{
            if(this->Dist == rhs.Dist){ ///最短距离相等应当选择换乘次数小的
                return this->TransferCnt > rhs.TransferCnt; ///由于是优先队列、重载小于号需要注意方向....
            }else{
                return this->Dist > rhs.Dist;
            }
        };
    };
    
    int cnt; ///边数量
    int Head[maxn]; ///邻接表头
    int Pre[maxn]; ///答案路径中每个点的前驱、便于恢复路径
    int Dis[maxn]; ///记录Dij中源点到其他点的最短路距离
    int TransNum[maxn]; ///到达这个点的时候换乘了多少次
    int Line[maxn][maxn]; ///记录路线信息
    int path[maxn<<2]; ///存储答案路径
    bool vis[maxn]; ///DIJ中的标记数组
    
    
    inline void init() ///初始化表头和计数变量
    {
        memset(Head, -1, sizeof(Head));
        cnt = 0;
    }
    
    inline void AddEdge(int from, int to) ///加边函数
    {
        Edge[cnt].v = to;
        Edge[cnt].nxt = Head[from];
        Head[from] = cnt++;
    }
    
    inline void Run_Dijkstra(int st, int en)
    {
        memset(vis, false, sizeof(vis));
        memset(Dis, 0x3f3f3f3f, sizeof(Dis));
        memset(TransNum, 0, sizeof(TransNum));
        priority_queue<NODE> que; while(!que.empty()) que.pop();
        Dis[st] = 0;
        que.push(NODE(st,0,0,0));
    
        while(!que.empty()){
            NODE T = que.top(); que.pop();
    
            if(vis[T.v]) continue;
            else vis[T.v] = true;
    
            for(int i=Head[T.v]; i!=-1; i=Edge[i].nxt){
                int Eiv = Edge[i].v;
                if(Dis[Eiv] > Dis[T.v] + 1){ ///满足松弛条件
    
                    Dis[Eiv] = Dis[T.v] + 1;
                    int NewTrans = (T.v==st ? 0 : T.TransferCnt + (Line[T.v][Eiv] != Line[T.Pre_v][T.v])); ///计算新的换乘次数
    
                    que.push(NODE(Eiv, Dis[Eiv], T.v, NewTrans));
                    Pre[Eiv] = T.v; ///记录前驱、便于恢复路径
                    TransNum[Eiv] = NewTrans; ///记录当前点的换乘次数
                }
                else if(Dis[Eiv] == Dis[T.v] + 1 && ///最短距离与松弛后相等则接下来比较换乘次数
                        TransNum[Eiv] > T.TransferCnt + (Line[T.v][Eiv] != Line[T.Pre_v][T.v])){
    
                    que.push(NODE(Eiv, Dis[Eiv], T.v, T.TransferCnt + (Line[T.v][Eiv] != Line[T.Pre_v][T.v])));
                    Pre[Eiv] = T.v; ///改变前驱
                    TransNum[Eiv] = T.TransferCnt + (Line[T.v][Eiv] != Line[T.Pre_v][T.v]); ///更新换乘次数
                }
            }
        }
    
        if(Dis[en] == 0x3f3f3f3f){ ///不可达、输出 No Solution
            puts("Sorry, no line is available.");
            return ;
        }else{
            int top = 0; ///记录路径中节点个数
            int now = en; ///由于记录的是前驱、所以从终点开始恢复路径
            int StNext;
            path[top++] = en;
            while(now != st){
                int temp = Pre[now];
                if(temp==st) StNext = now; ///记录起点的后继 ==> 我接下来的输出满足题目所需的答案有需要
                path[top++] = temp;
                now = temp;
            }
    
            printf("%d
    ", Dis[en]); ///先输出最短距离
            int CurLine = Line[st][StNext]; ///从起点开始记录当前所在的铁路编号
            int CurPoint = st; ///当前的点
            for(int i=top-1; i>=1; i--){
                if(Line[path[i]][path[i-1]] == CurLine) continue; ///如果下一个点和仍然在和之前一样的铁路编号则说明不是换乘点
                else{
                    printf("Go by the line of company #%d from %04d to %04d.
    ",CurLine, CurPoint, path[i]); ///输出格式需要注意....
                    ///printf("Go by the line of company #%d from %d to %d.
    ", CurLine, CurPoint, path[i]); ///!!!错误的输出格式!!!
                    CurPoint = path[i]; ///更新 CurPoint、CurLine
                    CurLine = Line[path[i]][path[i-1]];
                }
            }
            printf("Go by the line of company #%d from %04d to %04d.
    ",CurLine, CurPoint, path[0]);
           ///printf("Go by the line of company #%d from %d to %d.
    ", CurLine, CurPoint, path[0]); ///!!!错误的输出格式!!!
        }
    }
    
    int main(void)
    {
        init();
        int n;
        scanf("%d", &n);
        for(int i=1; i<=n; i++){
            int num, A, B;
            scanf("%d %d", &num, &A);
            for(int j=1; j<num; j++){
                scanf("%d", &B);
                AddEdge(A, B);
                AddEdge(B, A);
                Line[A][B] = Line[B][A] = i;
                A = B;
            }
        }
    
        int Query;
        scanf("%d", &Query);
        while(Query--){
            int A, B;
            scanf("%d %d", &A, &B);
            Run_Dijkstra(A, B);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Linux内核分析第七周学习笔记——Linux内核如何装载和启动一个可执行程序
    <深入理解计算机系统>第七章读书笔记
    Linux内核分析第六周学习笔记——分析Linux内核创建一个新进程的过程
    linux内核分析第3章&第18章读书笔记
    Linux内核分析第五周学习总结——分析system_call中断处理过程
    课本第五章读书笔记
    Linux内核分析第四周学习总结——系统调用的工作机制
    课本第一二章读书笔记
    20162328蔡文琛 实验二 树
    20162328蔡文琛 大二week07
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/8072780.html
Copyright © 2011-2022 走看看