算法学习记录(三十七)

183 阅读3分钟

问:

  1. 规定字符串只由'0','1'组成,若某一个字符串中,每个'0'的左边是都是'1',这种字符串就算达标。给定一个正数N,返回所有长度为N的字符串中,达标字符串的数量

  2. 有一个木棍数组arr,数组长度为n,第i根木棍长度为i+1。譬如arr=[1,2,3],含义为有三根棍子长度分别为1,2,3。请问最少去掉多少个木棍,可以使得任意三根木棍都不能组成三角形

  3. 给定一个背包容量为w和一个零食数组arr。数组中每一项的值代表零食的体积,请问有多少种放置方法可以使得零食总体积不超过背包容量

  4. 给定一个JobArr,其中每一项是一个岗位节点对象,岗位节点的信息是hard和money。分别代表岗位的难度和报酬(岗位是无限的,多个人可以选同一个岗位)。 再给定一个peopleArr,其中每一项代表一个人的能力值。当能力大于等于岗位难度时,则可以胜任这个工作,请返回所有人能拿到的最高报酬。

    interface JobArr {
        hard: string,
        money: number
    }
    type peopleArr = number[]
    

解: 1.

function getAllNums(N) {
    // 1个或者2个都只有一种放法
    if (N === 1 || N === 2) return 1
    // 对于每个0来说,都有一个1在左边
    // 那么可以得到一个结论,N个长度,第0位一定是1。所以F(N)相当于求F(N-1),F(n-1)有两种放置方法,
    // 第0位放0或者放1。
    // 放1就是该情况下的F(N-1),如果放0,则后续是F(N-2)
    // 假设N = 8,有7位数自由选择。
    // 1 1|0 ? ? ? ? ? ?
    // 当第1位等于1的时候,后续自由选择
    // 当第1位等于0的时候,后一位固定只能选1.所以是再加上F(6)的情况
    return getAllNums(N - 1) + getAllNums(N - 2)
}
function getMinNum(arr) {
    const N = arr.length
    // 形成斐波那契数即可
    // 假设是17根棍子。斐波那契数是前两项之和。刚好满足第三边不大于两边和的最小情况。
    // [1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17]
    // 斐波那契数
    let fb = 0
    // 斐波那契数的个数
    let fbNums = 0
    let one = 1
    let two = 1
    // 生成斐波那契数
    while (fb + res[res.length - 1] <= N) {
        fb = one + two
        one = two
        two = fb
        fbNums++
    }
    return N - fbNums
}
function getAllSumDp(arr, w) {
    const dp = []
    for (let i = 0; i <= arr.length; i++) {
        dp[i] = []
    }
    for (let i = arr.length; i >= 0; i--) {
        for (let j = 0; j <= w; j++) {
            if (i === arr.length) {
                dp[i][j] = 1
                continue
            }
            dp[i][j] = dp[i + 1][j] + (dp[i + 1][j + arr[i]] ?? 0)
        }
    }
    let sum = 0
    for (let i = 0; i < dp[0].length;i++) {
        sum += dp[0][i]
    }
    return sum
}
  1. 岗位按难度升序,同难度按酬劳降序,只保留同难度的第一个元素。然后再剔除掉难度增加酬劳不加的元素。最后结果就是难度升序并且酬劳也升序的job数组。每一个人只需要倒序遍历job数组,能力够了直接拿,否则就看下一项。
function getAllMax(jobArr, peopleArr) {
    // 给岗位按两种条件排序
    jobArr.sort((a, b) => {
        return a.hard - b.hard || b.money - a.money
    })
    // 只拿同hard的情况下第一个数据(即同难度下报酬最高的)
    const hardSet = new Set()
    jobArr = jobArr.filter((item, idx, arr) => {
        if (!hardSet.has(item.hard)) {
            hardSet.add(item.hard)
            return true
        }
        return false
    })
    // 难度较低岗位的薪酬
    let preMoney = 0
    // 剔除掉难度增加,报酬不加的
    jobArr = jobArr.filter((item) => {
        if (item.money > preMoney) {
            preMoney = item.money
            return true
        }
        return false
    })
    const res = []
    // 给各位选手按能力排序
    peopleArr.sort((a,b) => a - b)
    // 从哪个工作开始看
    let idx = jobArr.length - 1
    // 首先倒序遍历。能力强的选手先看工作。看工作也是倒序看。也就是先看薪酬高的工作,满足要求直接拿下
    // 然后轮到下一个选手,直接从上一个选手看到的位置开始看,因为这个选手不可能比上一个选手能力强,所以无须再看一遍工作
    // 这样可以从暴力遍历的O(n*m)优化至O(n+m)
    for (let i = peopleArr.length - 1; i >= 0; i--) {
        for (let j = idx; j >= 0; j--) {
            // 如果当前选手能力大于等于工作的难度,直接拿下
            if (peopleArr[i] >= jobArr[j].hard) {
                res.unshift(jobArr[j].money)
                // idx重新赋值
                idx = j
                break
            }
            // 当走到最后一个工作,表明他拿不到工作了,结果数组加个0薪酬
            if (j === 0) {
                res.unshift(0)
            }
        }
    }
    return res
}