On dynamic programming and dfs

Posted by anhedonia on Sun, 09 Jan 2022 06:34:48 +0100

preface

It is still necessary to learn the front-end algorithm. Not to mention that large factories test the algorithm in interviews, but when doing the permission module, if you don't understand the algorithm, you can't write the user-defined traversal of the tree structure.
Today, I will make a summary of the dynamic programming and dfs encountered recently, hoping to help you learn the algorithm.

Relationship between DP and dfs

Dynamic programming (dp):

  • Is a bottom-up traversal.
  • You need a recursive formula, such as f(n)=f(n-1)+f(n-2).
  • No recursion, just ordinary iteration, so the performance is good.

Depth first search (dfs):

  • Is a recursion from top to bottom.
  • We also need a recurrence formula, such as f(n)=f(n-1)+f(n-2).
  • Recursion from top to bottom. After encountering termination conditions, it will continue to take values from bottom to top (the essence of recursion). The process is similar to dp
  • Because it traverses all possibilities, its performance is particularly poor and it is generally pruned
  • The general pruning technique is to terminate or memorize in advance according to conditions

contact

  • The memorized dfs is essentially similar to dp
  • Because both of them need recursive formulas, in theory, all the problems dp that dfs can solve can be solved
  • However, some problems are suitable for dfs, such as solving the problem of possibility

Double solutions to a classic problem

leetcode322. Change money

dp

var coinChange = function(coins, amount) {
/**
    Ergodic i
    dp(n) = min( dp(n-conin[i]) + 1) 
    */
    coins.sort((a,b)=>b-a)
    const arr = new Array(amount+1).fill(0)
    for(let i =1;i<=amount;i++){ 
        let curAmount = i //total
        for(let j=0;j<coins.length;j++){
            let curConin = coins[j] //Current coin exchange
            if(curConin<=curAmount){
                let res = arr[curAmount-curConin]+1 //This number must be greater than or equal to 0
                if(res===0) continue //Equal to 0, this change is not advisable
                arr[i] = arr[i]===0?res:Math.min(arr[i],res) //Equal to 0 has not been initialized yet. Initialize it first
            }
        }
        if(!arr[i]) arr[i]=-1
    }
    // console.log(arr)
    return arr[amount]
    
};

dfs

If you do not prune, you will timeout. If you prune with the cache, the general efficiency of pruning will not be able to beat Dp unless there is a very good pruning strategy

var coinChange = function(coins, amount) {
        coins.sort((a,b)=>b-a)
        let cache = {}
        const dfs = (restamount)=>{

            if(restamount===0) return 0
            if(restamount<coins[coins.length-1])  return -1 //If the balance is smaller than the minimum denomination, it cannot be successfully redeemed
            if(cache[restamount])return cache[restamount]
            if(restamount<coins[coins.length-1]) return 
            for(var i=0;i<coins.length;i++){
                if(coins[i]<=restamount){
                    let res = dfs(restamount-coins[i])+1
                    if(res===0) continue
                    cache[restamount] = cache[restamount]?Math.min(cache[restamount],res):res
                }
            }
            // When we get here, the cache[restamount] has actually determined the top-down depth first traversal. In fact, there is a bottom-up process by default, so it is naturally slower than dp
            if(!cache[restamount]) cache[restamount]=-1
            return cache[restamount]
        }
        return dfs(amount)
};

Topics suitable for dsf

leetcode46. Full arrangement

Solving the problem of possibility is very suitable for dfs
If you use dp, the code will be difficult to organize

var permute = function(nums) {
    //dfs  
    /**
        dp(n) = dp(n-1)Each element in the array adds n at position i (0 < = i < = n)
     */
    const dfs=(nums)=>{ // [1,2]
        if(nums.length===1) return [nums.slice(0,1)]
        const arr = dfs(nums.slice(0,-1))
        const res = []
            arr.forEach(item=>{
                for(var i=0;i<=item.length;i++){
                    let addedItem = item.slice()
                    addedItem.splice(i,0,nums[nums.length-1])
                    res.push(addedItem)

                }
            })
        return res
    }
    return dfs(nums)
};

Topics suitable for dp

leetcode198. raid homes and plunder houses

This problem is very refreshing to solve with dp. Of course, the recursive formula contains greedy thought.

var rob = function(nums) {
    //DP (N) = max (DP (N-2) + nums [N], DP (N-1)), there are N in total, greedy. Income when it is the turn of the nth house from left to right

    const dp = new Array(nums.length).fill(0)
    dp[1] = nums[0]
    for(let i=2;i<=nums.length;i++){
        dp[i] = Math.max(dp[i-2]+nums[i-1] , dp[i-1]) //It's the turn of the income of house i
    }

    return dp[nums.length]
};

Topics: Javascript Front-end Algorithm