LeetCode打卡day20——网络空闲的时刻

80 阅读4分钟

“Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。”

一、题目描述:

2039. 网络空闲的时刻

给你一个有 n 个服务器的计算机网络,服务器编号为 0 到 n - 1 。同时给你一个二维整数数组 edges ,其中 edges[i] = [ui, vi] 表示服务器 ui 和 vi 之间有一条信息线路,在 一秒 内它们之间可以传输 任意 数目的信息。再给你一个长度为 n 且下标从 0 开始的整数数组 patience 。

题目保证所有服务器都是 相通 的,也就是说一个信息从任意服务器出发,都可以通过这些信息线路直接或间接地到达任何其他服务器。

编号为 0 的服务器是 主 服务器,其他服务器为 数据 服务器。每个数据服务器都要向主服务器发送信息,并等待回复。信息在服务器之间按 最优 线路传输,也就是说每个信息都会以 最少时间 到达主服务器。主服务器会处理 所有 新到达的信息并 立即 按照每条信息来时的路线 反方向 发送回复信息。

在 0 秒的开始,所有数据服务器都会发送各自需要处理的信息。从第 1 秒开始,每 一秒最 开始 时,每个数据服务器都会检查它是否收到了主服务器的回复信息(包括新发出信息的回复信息):

  • 如果还没收到任何回复信息,那么该服务器会周期性 重发 信息。数据服务器 i 每 patience[i] 秒都会重发一条信息,也就是说,数据服务器 i 在上一次发送信息给主服务器后的 patience[i] 秒 后 会重发一条信息给主服务器。
  • 否则,该数据服务器 不会重发 信息。
  • 当没有任何信息在线路上传输或者到达某服务器时,该计算机网络变为 空闲 状态。

请返回计算机网络变为 空闲 状态的 最早秒数 。

二、思路分析:

虽然题目看起来很复杂,但我们仔细阅读后能得出两个要点:

  1. 首先我们得知道网络各个服务器之间的连通情况和距离
  2. 根据服务器之间的连接情况,推导出最后一个发消息的服务器,变为空闲的时间,即为变为 空闲 状态的 最早秒数。

首先解决第一个问题,如何得到各个服务器之间的连通情况?这就是求图的起点到其他个点的最小距离,因为本题是无向图,单纯的广度遍历即可,距离就是由1一直加1到最远距离,可以写在循环里面。为了知道起点到各个服务器距离是多少,得通过题目给的edges[i] = [ui, vi] ,得到各个服务器的连通情况,我们假设使用二维数组distance,distance[i]表示与服务器i相邻的服务器数组。

其次就是要计算,一个服务器v什么时候变空闲?在0s时v发送消息,到达主服务器;主服务器立即处理回复,到达服务器v,这个是2* dist,dist表示它们之间的距离。那么当主服务器消息到达时,服务器v有没有重复发送信息呢?这就要进行分类讨论

  1. 当patience[i]>=2* dist时,服务器没有重复发送消息,故它在 2* dist+1时空闲
  2. 当patience[i]<2* dist时,服务器v肯定重复发送了,那么重复多少次呢?
    • 因为当patience[i]===2* dist时是服务器不会重复发送,故求重复发送的次数就为2dist1//patience[i](2* dist-1)//patience[i] ,
    • 那么最后一次重发的时间就为((2dist1//patience[i]))patience[i])((2* dist-1)//patience[i]))*patience[i])
    • 故服务器v变空闲说要的时间为((2dist1//patience[i]))patience[i])+2dist+1((2* dist-1)//patience[i]))*patience[i])+2* dist+1 那么最后我们的答案就是各个服务器空间时间的最大值。

那么我们怎么开始遍历整个网络?首先得有一个数组queue储存依据距离遍历服务器的顺序,遍历完一个服务器加入它周围的服务器,dist++这样下次遍历他们时就得到距离起点的距离。同时我们需要得知遍历过的服务器,假设用布尔数组。

那么我们的代码如下

/**
 * @param {number[][]} edges
 * @param {number[]} patience
 * @return {number}
 */
var networkBecomesIdle = function(edges, patience) {
    let n=patience.length
    let distance=new Array(n).fill(0).map(()=>new Array())
    for(let edge of edges){
        distance[edge[0]].push(edge[1])
        distance[edge[1]].push(edge[0])

    }
    let via=new Array(n).fill(false)
    via[0]=true
    let queue=[0],dist=1,ans=0
    while(queue.length){
        let size=queue.length
        for(let i=0;i<size;i++){
            let cur=queue.shift()
            for(let node of distance[cur]){
                if(via[node])continue;
                queue.push(node)
                via[node]=true
                let lasttime=patience[node]*Math.floor((2*dist-1)/patience[node]);
                let time=lasttime+2*dist+1
                ans=Math.max(ans,time)
            }

        }
        dist++;
    }
    return ans
};

三、优化

以上的写法,思路上是没有问题的,但是因为js是解释性语言,不同于java等强类型语言,本身所耗的时间就多,最后一个测试样例会超时。

超时的主要原因是使用 shift方法,我们使用index指针来进行优化,index指针指向要遍历的值,这样就不需要出栈的操作,节约空间

/**
 * @param {number[][]} edges
 * @param {number[]} patience
 * @return {number}
 */
var networkBecomesIdle = function(edges, patience) {
    let n=patience.length
    let distance=new Array(n).fill(0).map(()=>new Array())
    for(let edge of edges){
        distance[edge[0]].push(edge[1])
        distance[edge[1]].push(edge[0])

    }
    let via=new Array(n).fill(false)
    via[0]=true
    let queue=[0],dist=1,ans=0,index=0
    while(index<queue.length){
        let size=queue.length
        while(index<size){
            let cur=queue[index++]
            for(let node of distance[cur]){
                if(via[node])continue;
                queue.push(node)
                via[node]=true
                let lasttime=patience[node]*Math.floor((2*dist-1)/patience[node]);
                let time=lasttime+2*dist+1
                ans=Math.max(ans,time)
            }

        }
        dist++;
    }
    return ans
};

四、总结

        distance[edge[0]].push(edge[1])
        distance[edge[1]].push(edge[0])

为什么要两者都加入?因为我们的dist是在while循环中逐渐增加的,就算edge[1]中只有edge[0],也需要加入,不然空数组会报错。