算法学习记录(二十九)

157 阅读2分钟

问:

  1. 给定一个每一项都是正整数的数组,现在有两个玩家A,B。玩家只可以拿数组的开头或者结尾的数字,A先B后。请问他们能拿到的最高分是多少。
  2. 一个M * N 的棋盘上,有一个棋子马(马走日),从(0,0)位置出发,走K步到达(x,y)位置,有多少种走法
  3. 一个正整数数组,数组中每一项代表一个面值,该面值可以选任意张。给一个target,有多少种方法可以累加到target。譬如:arr=[1,2],target=3。可以选3个1,或者1个1+1个2。即一共两种方法

解:

  1. 递归改DP
// 暴力递归
function smartPeople(arr) {
    const left = 0
    const right = arr.length - 1
    // 先手函数
    function firstHand(left, right) {
        if (left === right) {
            return arr[left]
        }
        const p1 = arr[left] + secondHand(left + 1, right)
        const p2 = arr[right] + secondHand(left, right - 1)
        return Math.max(p1, p2)
    }
    // 后手函数
    function secondHand(left, right) {
        if (left === right) {
            return 0
        }
        const p1 = firstHand(left + 1, right)
        const p2 = firstHand(left, right - 1)
        return Math.min(p1, p2)
    }
    return Math.max(firstHand(left, right), secondHand(left, right))
}

// 递归改dp
function smartPeople (arr) {
    const dp1 = []
    const dp2 = []
    for (let i = 0; i < nums.length; i++) {
        dp1[i] = []
        dp2[i] = []
        dp1[i][i] = nums[i]
        dp2[i][i] = 0
    }
    for (let i = nums.length - 2; i >= 0; i--) {
        for (let j = i + 1; j < nums.length; j++) {
            dp1[i][j] = Math.max(dp2[i + 1][j] + nums[i], dp2[i][j - 1] + nums[j])
            dp2[i][j] = Math.min(dp1[i + 1][j], dp1[i][j - 1])
        }
    }
    return dp1[0][nums.length - 1] >= dp2[0][nums.length - 1]
}
// 暴力递归
function moveMethods(m, n, x, y, k) {
    // 当前坐标和剩余步数
    function getRes(curX, curY, surplusStep) {
        if (curX < 0 || curX > m || curY < 0 || curY > n){
            return 0
        }
        // 从(0,0)到(X,Y)等同于从(X,Y)到(0,0),所以base case也可设置为走到0,0时返回一条结果
        if (surplusStep === 0) {
            return (curX === 0 && curY === 0) ? 1 : 0
        }
        const res = getRes(curX + 1, curY + 2, surplusStep-1) +
            getRes(curX + 2, curY +1, surplusStep-1)+
            getRes(curX + 2, curY - 1, surplusStep-1)+
            getRes(curX + 1, curY - 2, surplusStep-1)+
            getRes(curX - 1, curY - 2, surplusStep-1)+
            getRes(curX - 2, curY - 1, surplusStep-1)+
            getRes(curX - 2, curY + 1, surplusStep-1)+
            getRes(curX - 1, curY + 2, surplusStep-1)
        return res
    }
    return getRes(x, y, k)
}

// 递归改DP
function moveMethods2(m, n, x, y, k) {
    if (x < 0 || x > m || y < 0 || y > n){
        return 0
    }
    const dp = []
    // 生成三维表
    for (let step = 0; step <= k; step++){
        dp[step]=[]
        for (let row = 0; row <= m; row++) {
            dp[step][row]=[]
        }
    }
    // 根据base case初始化dp表
    dp[0][0][0] = 1
    // 如下图所示,根据base case可知,在K维度为0的时候,只有(0,0,0)处坐标值是1,其余全部为0
    // 根据递归依赖关系,K维度上同一层的数据,只依赖于它的下一层数据,所以K的维度从下往上遍历,每一层都可以计算
    // 得到每一个坐标的位置,最后到第K层时,返回(k,x,y)位置的数据即可
    for (let step = 1; step <= k; step++) {
        for (let row = 0;row <= m; row++) {
            for (let col = 0; col <= n; col++) {
                dp[step][row][col] = getValue(row + 1, col + 2, step-1)+
                getValue(row + 2, col + 1, step-1)+
                getValue(row + 2, col - 1, step-1)+
                getValue(row + 1, col - 2, step-1)+
                getValue(row - 1, col - 2, step-1)+
                getValue(row - 2, col - 1, step-1)+
                getValue(row - 2, col + 1, step-1)+
                getValue(row - 1, col + 2, step-1)
            }
        }
    }
    return dp[k][x][y]
    function getValue(row, col, step) {
        if (row < 0 || row > m || col < 0 || col > n){
            return 0
        }
        return dp[step][row][col] ?? 0
    }
}

无标题.png 3.

// 暴力递归
function getAllPlans(arr, target) {
    // cur: 当前遍历到的位置  need: 还需要多少钱
    function getRes(cur, need) {
        if (cur === arr.length) {
            return need === 0 ? 1 : 0
        }
        let res = 0
        // 遍历选择取多少张(即当前面值选0张,1张,2张....x张,各自递归下去累加起来)
        // 选择的张数*面值不要超过所需要的钱
        for (let i = 0; arr[cur] * i <= need; i++) {
            res += getRes(cur + 1, need - arr[cur] * i)
        }
        return res
    }
    return getRes(0, target)
}

// 递归改DP
function getAllPlans2(arr, target) {
    const dp = []
    // 创建二维表
    for (let i = 0; i <= arr.length; i++) {
        dp[i] = []
        for (let j = 0 ;j <= target ;j++) {
            if (i === arr.length) {
                dp[i][j] = 0
            }
            if (j === 0) {
                dp[i][j] = 1
            }
        }
    }
    // 由依赖关系可知,每一个数据依赖它下一层数据,所以从下往上,从左往右遍历
    for (let i = arr.length -1; i >=0;i--) {
        for (let j =0;j<=target;j++) {
            // 根据递归写法改造的第一版写法
            //dp[i][j]=0
            //for (let k = 0; arr[i] * k <= j; k++) {
            //    dp[i][j] += dp[i + 1][j - arr[i] * k] ?? 0
            //}
            
            // 第二版 DP斜率优化
            // 右下图关系可以看出,本层依赖的数据虽然是下一层的数据。
            //但是本层更靠左的数据,其实已经计算过一次了。所以当前数据就是
            //当前位置减去偏移量(面值)对应的项 + 当前位置正下方的项
            dp[i][j] = (dp[i][j-arr[i]] ?? 0 ) + dp[i+1][j]
        }
    }
    return dp[0][target]
}

无标题.png