zoukankan      html  css  js  c++  java
  • 航班预订统计,听说这题有的人看了题解还没懂?

    前言

    前几天,有一哥们发我一个LeetCode题目链接,紧跟着附上了自己的提交记录,一个2ms,另一个1451ms...

    我一看,这题有点意思啊,不同的思路竟然时间差这么多。搞它。

    题目描述

    这里有n个航班,它们分别从1n进行编号。

    我们这儿有一份航班预订表,表中第i条预订记录bookings[i] = [i, j, k]意味着我们在从ij的每个航班上预订了k个座位。

    请你返回一个长度为n的数组answer,按航班编号顺序返回每个航班上预订的座位数。

    示例:

    输入:bookings = [ [1,2,10], [2,3,20], [2,5,25] ], n = 5

    输出:[10,55,45,25,25]

    O(m*n)解法

    根据题意初始化长度为nanswer数组,代表1n号航班预订的座位数量,外层遍历 bookings,内层遍历bookings[i] = [i, j, k],计算航班号ij的座位数量,即当前座位数量加k

    public int[] corpFlightBookings(
            int[][] bookings, int n) {
    
        int[] answer = new int[n];
    
        // 遍历整个bookings数组
        for (int[] b : bookings) {
            // 内层循环把每个航班预订数加上
            for (int i = b[0] - 1;
                 i <= b[1] - 1; i++) {
                answer[i] += b[2];
            }
        }
        return answer;
    }
    

    O(n)解法

    思考

    O(m*n)解法中关键一点,内层循环我们一直重复的在[i, j]之间加上k,怎么将这循环变成O(1),成为问题的关键!

    [i, j]之间加上k,这让我想到了等差数列,这不就是公差为k的等差数列吗?然后呢?

    分析

    answer[i]表示第i个航班预订的座位数。定义一个差分数组d[]d[i]表示第i个航班与第i-1个航班预订座位的差值,即d[i] = answer[i] - answer[i - 1]

    这样,我们每次遍历到bookings[i] = [i, j, k],就只需要将d[i]增加kd[j + 1]减少k即可,因为ij之间,航班预订数量是没有变化的。

    最后,计算answer[i] = answer[i - 1] + d[i],返回answer即可。

    推演

    好吧,这样说可能有人没懂,我们按照题目的例子推演一次:

    • 初始航班预订数量数组 answer = [0,0,0,0,0],差分数组d = [0,0,0,0,0]
    • 当遍历到bookings[0] = [1,2,10]的时候,差分数组第1位加10,第3位减10,变成d = [10,0,-10,0,0]
    • 同理,当遍历到bookings[1] = [2,3,20]的时候,差分数组变成d = [10,20,-10,-20,0]
    • 当遍历到bookings[2] = [2,5,25]的时候,差分数组变成d = [10,45,-10,-20,0],第6位要减25,我们也不需要了
    • 最后计算answer数组的值,answer[0] = d[0] = 10answer[1] = d[1] + answer[0] = 45 + 10 = 55answer[2] = d[2] + answer[1] = -10 + 55 = 45...
    • 最最后发现,只申请一个数组表示d[]answer[]就可以了,over!

    代码

    public int[] corpFlightBookings(
            int[][] bookings, int n) {
    
        int[] answer = new int[n];
    
        // 遍历bookings 计算航班i+1 对航班i 变化的预订数
        for (int[] b : bookings) {
            // 增加的预订数
            answer[b[0] - 1] += b[2];
            // 防止数组越界
            if (b[1] < n) {
                // 减少的预订数量
                answer[b[1]] -= b[2];
            }
        }
    
        // 航班i的预订数等于,i-1的预订数,加i时刻变化的预定数
        for (int i = 1; i < n; i++) {
            answer[i] += answer[i - 1];
        }
        return answer;
    }
    

    拼车

    你以为这就完了吗?不要太天真。再来看一下这个题,或许会给你带来新思路。

    题目描述

    假设你是一位顺风车司机,车上最初有 capacity 个空座位可以用来载客。由于道路的限制,车 只能 向一个方向行驶(也就是说,不允许掉头或改变方向,你可以将其想象为一个向量)。

    这儿有一份行程计划表 trips[][],其中 trips[i] = [num_passengers, start_location, end_location] 包含了你的第 i 次行程信息:

    必须接送的乘客数量;
    乘客的上车地点;
    以及乘客的下车地点。
    这些给出的地点位置是从你的 初始 出发位置向前行驶到这些地点所需的距离(它们一定在你的行驶方向上)。

    请你根据给出的行程计划表和车子的座位数,来判断你的车是否可以顺利完成接送所用乘客的任务(当且仅当你可以在所有给定的行程中接送所有乘客时,返回 true,否则请返回 false)。

    示例 1:

    输入:trips = [[2,1,5],[3,3,7]], capacity = 4

    输出:false

    示例 2:

    输入:trips = [[2,1,5],[3,3,7]], capacity = 5

    输出:true

    提示:

    • 你可以假设乘客会自觉遵守 “先下后上” 的良好素质
    • trips.length <= 1000
    • 1 <= trips[i][0] <= 100

    题目分析

    这道题实际上就是问,车的座位数量是否能满足每个行程i的乘客,即每个乘客都坐上座位,能则返回true,否则返回false

    如果我们能计算出,行程i,要乘车的乘客的数量,然后跟capacity对比一下,就能得到答案了。

    很显然,要乘车的乘客的数量 = 车上原来乘客的数量 - 下车乘客数量 + 上车乘客数量

    思路

    我们可以用数组或者Map记录,行程i,下车乘客的数量和上车乘客的数量,然后行程开始到结束,计算要乘车的乘客数量,并与capacity比较。

    代码实现Map版

    public boolean carPooling(
            int[][] trips, int capacity) {
        // 使用TreeMap 是为了让元素根据key排序
        TreeMap<Integer, Integer> map =
                new TreeMap<>();
        for (int[] t : trips) {
            int v = map.getOrDefault(t[1], 0) + t[0];
            // 上车乘客数量
            map.put(t[1], v);
            v = map.getOrDefault(t[2], 0) - t[0];
            // 下车乘客数量
            map.put(t[2], v);
        }
    
        int cur = 0;
    
        for (Map.Entry<Integer, Integer> entry
                : map.entrySet()) {
            Integer value = entry.getValue();
            // 当前数量=之前数量+变化的数量
            cur += value;
    
            if (cur > capacity) {
                return false;
            }
        }
        return true;
    }
    

    代码实现数组版

    public boolean carPooling(
            int[][] trips, int capacity) {
    
        // 最远行程 数组长度
        int max = 0;
        for (int[] t : trips) {
            max = Math.max(max, t[2]);
        }
    
        // 所有要乘车的乘客数量
        int[] passengers = new int[max + 1];
        for (int[] t : trips) {
            passengers[t[1]] += t[0];
            passengers[t[2]] -= t[0];
        }
    
        int cur = 0;
        for (int passenger : passengers) {
            // 当前数量 = 之前数量 + 变化的数量
            cur += passenger;
            if (cur > capacity) {
                return false;
            }
        }
    
        return true;
    }
    

    回头

    拼车的代码与场景感觉好理解一些,因为生活中我们就是这样,先下后上,能不能坐上座,就看车上原来有多少人,还有下车多少人。

    我们再回来来看航班预订统计这题,实际上跟拼车是完全一样的题目。

    我看到有人问,计算bookings[i] = [i, j, k]预订变化数量的时候,为啥是第j + 1的位置要减k,而不是j的位置呢?因为,j - 1的位置,航班预订座位数量应该加k,而j的位置,航班预订座位数量也加k,所以jj - 1之间数量是没有变化的。但是,j + 1的位置航班数量不再加k了,所以j + 1相对于j位置航班预订数量是减少k的。

    拼车这道题,trips[i][j],在j位置,车到站了,乘客就下车了,再坐一站就过站了...

    总之,两道题本质是完全一样的,只不过略微有些细节不同。

    欢迎关注个人微信公众号【如逆水行舟】,用心输出基础、算法、源码系列文章。

  • 相关阅读:
    Freesql使用一张带父子关系的表,生成list树型数据
    css特效实现html表格显示部分内容,当鼠标移上去显示全部。
    ASP.NET Core如何自动生成小写的破折号路由
    windows服务+OpenSSH+SVN在Jenkins上的自动化部署进阶篇
    Windows+.Net Framework+svn+IIS在Jenkins上的自动化部署入门
    .net(C#)中结构和类的区别
    Web.config配置详解
    关于Asp.net core配置信息读取的源码分析梳理
    极简实用的Asp.NetCore框架再新增商城模块
    极简实用的Asp.NetCore模块化框架新增CMS模块
  • 原文地址:https://www.cnblogs.com/iisheng/p/12689110.html
Copyright © 2011-2022 走看看