“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一直加1到最远距离,可以写在循环里面。为了知道起点到各个服务器距离是多少,得通过题目给的edges[i] = [ui, vi] ,得到各个服务器的连通情况,我们假设使用二维数组distance,distance[i]表示与服务器i相邻的服务器数组。
其次就是要计算,一个服务器v什么时候变空闲?在0s时v发送消息,到达主服务器;主服务器立即处理回复,到达服务器v,这个是2* dist,dist表示它们之间的距离。那么当主服务器消息到达时,服务器v有没有重复发送信息呢?这就要进行分类讨论
- 当patience[i]>=2* dist时,服务器没有重复发送消息,故它在 2* dist+1时空闲
- 当patience[i]<2* dist时,服务器v肯定重复发送了,那么重复多少次呢?
- 因为当patience[i]===2* dist时是服务器不会重复发送,故求重复发送的次数就为,
- 那么最后一次重发的时间就为
- 故服务器v变空闲说要的时间为 那么最后我们的答案就是各个服务器空间时间的最大值。
那么我们怎么开始遍历整个网络?首先得有一个数组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],也需要加入,不然空数组会报错。