算法学习记录(七十七)

203 阅读1分钟

导弹拦截

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是 \le 50000≤50000 的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

image.png

解: 第一问算法原型是求最长递减子序列的长度。 学习记录的上一个文章是求最长递增子序列,稍微改一下即可

const minLength = (arr) => {
    const dp = [{
        key: arr[0],
        val: 1
    }]
    for (let i = 1; i < arr.length; i++) {
        findSmallAndClosest(arr[i])
    }
    return dp[dp.length - 1].val
    function findSmallAndClosest(target) {
        let left = 0
        let right = dp.length - 1
        let res = -1
        while (left <= right) {
            const mid = ~~((left + right) / 2)
            if (dp[mid].key <= target) {
                res = mid
                right = mid - 1
            } else {
                left = mid + 1
            }
        }
        if (res === -1) {
            dp[dp.length] = {
                key: target,
                val: dp[dp.length - 1].val + 1
            }
        } else {
            dp[res].key = target
        }
    }
}

第二问: 计算一轮最长递减子数组之后元素移除,再接着计算此时的最长递减子数组,直到数组清空,最长递减子数组的个数就是需要多少个拦截系统。但是这种方法需要重复遍历,并且会修改数组。

贪心解: 遍历数组,碰到一个导弹就选择刚好比它高的拦截系统去匹配它,若没有比它高的就配置一个新的拦截系统。

const getMinNum = (arr) => {
    const interceptArr = []
    for (let i = 0; i < arr.length; i++) {
        find(arr[i])
    }
    return interceptArr.length
    function find(target) {
        let left = 0
        let right = interceptArr.length - 1
        let res = -1
        while (left <= right) {
            const mid = ~~((left + right) / 2)
            if (interceptArr[mid] >= target) {
                res = mid
                right = mid - 1
            } else {
                left = mid + 1
            }
        }
        if (res === -1) {
            interceptArr[interceptArr.length] = target
        } else {
            interceptArr[res] = target
        }
    }
}