目录
补种未成活胡杨
考察移动窗口、双指针、hash
题目描述
近些年来,我国防沙治沙取得显著成果。某沙漠新种植N棵胡杨(编号1-N),排成一排。一个月后,有M棵胡杨未能成活。现可补种胡杨K棵,请问如何补种(只能补种,不能新种),可以得到最多的连续胡杨树?
输入描述
第一行:N总种植数量;
第二行:M未成活胡杨数量;
第三行:M个空格分隔的数,按编号从小到大排列
第四行:K最多可以补种的数量
其中:
1<=N<=100000
1<=M<=N
0<=K<=M
输出描述
最多的连续胡杨棵树
示例1
输入
5
2
2 4
1
输出
3
示例2
输入
13
4
1 4 8 9
1
输出
6
解析
使用双指针指向开头,尾指针向后逐个移动,保证头尾指针之间存在的未种活胡杨树为可补种树,当超过时,头指针向后移动,移动过程中记录窗口的最大长度,直到尾指针移动到最后一个元素结束。
答案
function getContinuousNum(str) {
let [N, M, Marr, K] = str.split('\n')
let hash = {}
Marr.split(' ').forEach(v => {
hash[v] = true
})
let start = 1
let end = 1
let res = 0
let dead = 0
// let live = 0
let len = 0
while (end <= N) {
if (hash[end]) {
dead++
}
len++
// 窗口中死的大于可补种树
while (dead > K && end >= start) {
if (hash[start]) {
dead--
}
start++
len--
}
if (end - start + 1 > res) {
res = end - start + 1
}
end++
}
return res
}
console.log(getContinuousNum(`5
2
2 4
1`))
console.log(getContinuousNum(`13
4
1 4 8 9
1`))
最大N个数与最小N个数的和
考察排序,set集合
题目描述
给定一个数组,编写一个函数来计算它的最大N个数与最小N个数的和。你需要对数组进行去重。
说明:数组中数字范围[0,1000]
最大N个数与最小N个数不能有重叠,如有重叠,输入非法返回-1
输入非法返回-1
输入描述
第一行输入M,M标识数组大小
第二行输入M个数,标识数组内容
第三行输入N,N表达需要计算的最大、最小N个数,输出最大N个数与最小N个数的和。
输出描述
输出最大N个数与最小N个数的和
示例1
输入
5
95 88 83 64 100
2
输出
342
解析
先使用set集合进行去重,判断去重后长度是否大于等于2*N,小于的话返回-1,大于的话去除数组前N个和后N个相加。
答案
function getSum(str) {
let [len, arr, N] = str.split('\n')
arr = arr.split(' ').map(v => Number(v))
// 去重
arr = [...new Set(arr)]
// 排序
arr.sort((a, b) => a - b)
if (arr.length >= 2 * N) {
// 取最大N个和最小N个
arr = [...arr.slice(0, N), ...arr.slice(len - N, len)]
return arr.reduce((v, t) => v + t)
}
return -1
}
console.log(getSum(`5
95 88 83 64 100
2`))
最长广播响应
考察set、深度遍历、递归、hash
题目描述
某通信网络中有N个网络节点,用1到N进行标识。网络中的节点互联互通,且节点之间的消息传递有时延,相连节点的时延均为一个时间单位。
现给定网络节点的连接关系link[i]={u,v},其中u和v表示网络节点。当指定一个节点向其它节点进行广播,所有被广播节点收到消息后都会在
原路径上回复一条响应消息,请计算发送节点至少需要等待几个时间单位才能收到所有被广播节点的响应消息。
注意
1.N的取值范围为[1,100]
2.连接关系link的长度不超过3000,且1<=u,v<=N;
3.网络中任意节点间均是可达的
输入描述
输入的第一行为两个正整数,分别表示网络节点的个数N,以及时延列表的长度i;接下来的i行输入,表示节点间的连接关系列表;最后一行的输入
为一个正整数,表示指定的广播节点序号;
输出描述
输出一个整数,表示发送节点接收到所有响应消息至少需要等待的时长。
示例1
输入
5 7
2 1
1 4
2 4
2 3
3 4
3 5
4 5
2
输出
4
示例2
输入
7 10
2 1
1 4
2 4
2 3
3 4
3 5
4 5
5 6
4 6
6 7
2
输出
6
解析
至少需要等待的时长等于到目标节点到其它节点中的最短路径的最大值。
1.将当前节点的可达节点用key-value的形式表示。通过访问对象的key的得到value用set表示,set中存储当前节点的可达节点序号。
2.使用深度遍历(递归)来查找是到达终点的最短路径。
我们用f(start,end)表示start到end的最短时长,首先需要找到f(start,end)和下一次的关系。等于start能到的所有节点出发的值中最小的一个。
即f(start,end)=min(f(start1,end),f(start2,end),f(start3,end)....),这里start1,start2,start3,为start能到的节点。
3.找终点。
1.走过的节点不能再走避免循环(通过set记录走过的节点)。
2.当前时延超过之前记录的最小时延时不需要继续往下了。
3.当start的可达节点中有end时不需要往下继续找了。
4.中间可以记录已经得到节点之间的最短值,因为这里需要查找目标节点和每个节点之间的最短路径,可以避免重复计算。
答案
function getMaxBroadcast(str) {
let arr = str.split('\n')
let [n, len] = arr.shift().split(' ')
target = arr.pop()
// 用于存储节点之间的关系
let hash = {}
for (let i = 0; i < len; i++) {
let [a, b] = arr[i].split(' ')
if (hash[a]) {
hash[a].add(b)
} else {
hash[a] = new Set(b)
}
if (hash[b]) {
hash[b].add(a)
} else {
hash[b] = new Set(a)
}
}
let max = 0
// 用于记录已有结果的最短路径,避免每个节点和目标节点计算最短路径时需要重复递归计算
let delay = {}
Object.keys(hash).forEach(v => {
if (v === target) {
return
}
let time = calcMinLen(v, target, Infinity, new Set(), 0, delay, hash)
// 记录已算出结果的最短路径
delay[v + '-' + target] = time
delay[target + '-' + v] = time
if (time > max) {
max = time
}
})
return max * 2
}
function calcMinLen(start, end, res, had, time, delay, hash) {
// 当前路径可以到达终点返回结果
if (hash[start].has(end)) {
return time + 1
}
// 当前时间已经大于已有结果的路径,即该路径非最短路径
if (time >= res) {
return
}
for (let node of hash[start]) {
// 已经走过的节点
if (had.has(node)) {
continue
}
// 添加走过的路径避免循环
had.add(start)
time++
// 记录结果中已经存在的直接采用,没有的递归查询最短路径
let tmp = delay[node + '-' + end] ? delay[node + '-' + end] : calcMinLen(node, end, res, had, time, delay, hash)
if (tmp + time < res) {
res = tmp + time
}
// 下次循环还原走过的节点和时间
had.delete(start)
time--
}
// 记录已算出结果的最短路径
delay[start + '-' + end] = res - time
delay[end + '-' + start] = res - time
return res
}
console.log(getMaxBroadcast(`5 7
2 1
1 4
2 4
2 3
3 4
3 5
4 5
2`))
console.log(getMaxBroadcast(`7 10
2 1
1 4
2 4
2 3
3 4
3 5
4 5
5 6
4 6
6 7
2`))