PTA L2-043龙龙送外卖

9 篇文章 0 订阅
6 篇文章 0 订阅

4.1.6 L2-043龙龙送外卖

  • 题目地址:https://pintia.cn/problem-sets/994805046380707840/exam/problems/1518582482059845632

  • 题目概述:

    这是一道关于求树中各个结点深度的题,两种方法:用DFS(将输入转为孩子表示法);用回溯法(从结点往上找双亲结点统计出结点的距离)。题目每新增一个外卖点,就是将之前的点一起结合起来探寻最短路径。

    首先这是一棵树,单看每个结点它自己的最短路径就是自身的深度-1(根结点深度为1),所以主要问题就是怎么合理安排这些外卖点的访问顺序让总体送外卖的距离最短。

    因为可能有些点不在同一个分支上,就会造成访问完然后再返回到分支处访问另一个结点。可以确定的是每个结点到外卖点的距离是唯一的,所以我们要做的就是让外卖员返回的路程尽量的少,因为最后不需要返回到外卖点,所以我们可以将深度最深的点留到最后去找,这样就不需要再返回一次增加额外更多的距离了。

  • 主要操作:

    采用的是回溯法求结点,不过会将回溯过程中经过的结点顺便求出它的深度。需要一个max值存储前一次的最大深度,这决定了哪条分支会最后被访问。

    1. 要是本身这个点前面就求出了深度了,说明这个点送外卖时肯定访问过,而题目又说送的顺序任意,所以直接就先送这个,那么距离就不会增加(与该点之前求得的距离相同)。
    2. 要是整条路都没遇到已经求过的点,说明这条分支从未被访问过。如果此点的距离>max,,那就需要从max点返回再走向当前点。如果此点的距离<=max,说明该点先需要被访问,回去再回到外卖点,两倍的该点的距离(深度-1)
    3. 要是遇到一个点回溯过程中遇到已知深度的点,就可以结束回溯,直接将已知深度 与 目前回溯的步数相加即可得到所求点的深度。此时会出现出现2种情况。
      1. max<当前点距离,说明此时当前点应该最后被访问,max点必须再走一遍max距离回到外卖点再到当前点。而上一次的情况,当前点这个分支(碰到已知深度的点作为分叉点)到外卖点肯定多走了一次,需要减去一次,然后再加上分叉点到当前的距离即可。
      2. max>=当前点距离,说明这个点需要来回两次(不是最后被访问),所以直接上一次情况+2倍的分叉点到当前的距离。
  • 代码:

    #include <iostream>
    using namespace std;
    /*
        以后不管怎么样,尽量把数组开大点。
    */
    int N, M;
    int node[100010];
    // int inRoad[100010];
    int level[100010];
    int temp[100010];
    int main()
    {
        cin >> N >> M;
        for (int i = 1; i <= N; i++)
        {
            cin >> node[i];
        }
    
        int max = 0;
        int sumOfRoadDis = 0;
        int count2Root = 0, count2Same = 0;
        int point = M;
        int x;
        while (point--)
        {
            count2Root = 0;
            count2Same = 0;
            cin >> x;
            int parent = x;
            // 说明访问过,直接输出先前的距离
            if (level[x] != 0)
            {
                cout << sumOfRoadDis << endl;
                continue;
            }
    
            // 从这个点的父亲开始访问循环
            // 这个是最花时间的
            while (parent != -1)
            {
                if (level[parent] == 0)
                {
                    temp[count2Same] = parent;
                    // inRoad[parent] = 1;
                    count2Same++;
                }
                else
                {
                    break;
                }
                count2Root++;
                parent = node[parent];
            }
    
            // 得到了从外卖点到x的路径长度,也确定了途中经过的点。
            // 此时parent的值要么为-1,要么这个parent是被访问过的,并且它的距离也知道。
    
            if (parent == -1)
            {
                int length = count2Same - 1;
                for (int i = 0; i < count2Same - 1; i++)
                {
                    level[temp[i]] = length;
                    length--;
                }
            }
            else
            {
                int length = count2Same;
                for (int i = 0; i < count2Same; i++)
                {
                    level[temp[i]] = level[parent] + length;
                    length--;
                }
            }
            count2Root = level[x] + 1;
    
            // 说明途中压根就没遇到经过点
            if (count2Root == count2Same)
            {
                count2Root--;
                if (count2Root > max)
                {
                    sumOfRoadDis = sumOfRoadDis + max + count2Root;
                }
                else
                {
                    sumOfRoadDis = sumOfRoadDis + 2 * count2Root;
                }
            }
            // 说明经过了点。
            else
            {
                count2Root--;
                if (count2Root <= max)
                {
                    sumOfRoadDis += count2Same * 2;
                }
                else
                {
                    sumOfRoadDis += max - count2Root + count2Same * 2;
                }
            }
            cout << sumOfRoadDis << endl;
            if (count2Root > max)
            {
                max = count2Root;
            }
        }
        return 0;
    }
    
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值