算法学习记录(三十)

98 阅读2分钟

问:

  1. 给定一个有序正数数组,其中每一项代表它在X轴上的坐标。现在有一个长度为L的绳子,请问它最多可以覆盖几个项
  2. 给定一个正数N,请用6和8这两种数拼出来,若能拼出来,返回最少用几个数,若不能则返回-1。例:N = 24,可以用4个6或者3个8。那么最少用3个数
  3. 有先手和后手两个动物吃草,每次只能按照4的次方来吃(1,4,16,64,...)。给一个草堆大小为N,在谁的回合把这个草吃完,谁就获胜。请返回获胜的动物
  4. 给一个字符串,包含R,G两种字符,可以把R,G互相转换,若要变成左侧都是G,右侧都是R,最少转换几个字符。例:GRGRGRR 转换结果为GGGGGRR,最少转换2个字符;例2:RRRRR,最少转换0个字符

解:

  1. 滑动窗口,判断窗口差值是否小于等于L,是的话就计算一次最大值,然后窗口右扩,否则窗口左收
function getMaxItem(arr, L) {
    let left = 0
    let right = 0
    let max = 0
    while (right < arr.length) {
        if (left > right) right = left
        if (l >= arr[right] - arr[left]) {
            max = Math.max(max, right - left + 1)
            right++
        } else {
            left++
        }
    }
    return max
}
  1. 贪心策略,先尽可能多的要8,剩余的数用6来配,若配不出来,就依次减少8的数量。
function getMinNums(N) {
    let eightNums = ~~(N / 8)
    let sixNums = 0
    while (eightNums >= 0) {
        const need = N - eightNums * 8
        // 超过24的值不必再讨论,因为24是6,8的最小公倍数,24的部分应该被8处理,而不是现在被6处理
        if (need > 24) return -1
        if (need % 6 === 0) {
            sixNums = need / 6
            return sixNums + eightNums
        } else {
            eightNums--
        }
    }
    return -1
}
  1. 先手动物从1份开始吃,递归判断能否赢,不能就吃4份,16份,64份。。。若没有赢的情况,那就是后手赢
function getWinAnimal(N) {
    function getRes(need) {
        if (need === 0) {
            return '后手'
        }
        for (let i = 0; 4 ** i <= need; i++) {
            // 如果递归告诉我后手赢了,那就意味着先手赢了(因为此时我已经吃过一轮,变成了后手)
            if (getRes(need - 4 ** i) === '后手') {
                return '先手'
            }
        }
        // 没有赢的情况,就是后手赢
        return '后手'
    }
    return getRes(N)
}
// dp解
function getWinAnimal(N) {
    const dp = ['后']
    for (let i = 1; i <= N; i++) {
        dp[i] = '后'
        for (let j = 0; 4 ** j <= i; j++) {
            if (dp[i - 4 ** j] === '后') {
                dp[i] = '先'
            }
        }
    }
    return dp[N]
}
function getMinChangeChar(str) {
    let minNum = Infinity
    // 暴力解法中每次都要判断左右分别有多少个R和G
    // 所以预处理,提前计算好每个位置的数据
    const preLeftArr = []
    const preRightArr = []
    for (let i = 0;i<str.length;i++){
        // 判断[0,i]范围内有多少个R
        if (str[i] === 'R') {
            preLeftArr[i] = (preLeftArr[i-1] ?? 0) + 1
        } else {
            preLeftArr[i] = preLeftArr[i-1] ?? 0
        }
    }
    for (let i = str.length -1;i>=0;i--){
        // 判断[i,N]范围内有多少个G
        if (str[i] === 'G') {
            preRightArr[i] = (preRightArr[i+1] ?? 0) + 1
        } else {
            preRightArr[i] = preRightArr[i+1] ?? 0
        }
    }
    // left区域的分界线从-1开始,即全部都当做右区域
    for (let i = -1; i <= str.length; i++) {
        let tempNum = 0
        // 全部都当做右区域
        if (i === -1) {
            tempNum = preRightArr[0]
            continue
        } 
        if (i === str.length) {
            tempNum = preLeftArr[i-1]
            continue
        }
        tempNum = preLeftArr[i] + (preRightArr[i+1] ?? 0);
        minNum = Math.min(tempNum, minNum)
    }
    return minNum
}