【LeetCode专项】动态规划、背包问题

107 阅读1分钟

70. 爬楼梯

代码

func climbStairs(n int) int {
    if n==1 {
        return 1
    } else if n==2 {
        return 2
    }

    ans1, ans2 := 1,2
    tmp := 0
    n-=2
    for n>0 {
        tmp = ans2

        ans2+=ans1
        ans1=tmp

        n--
    }
    return ans2
}

思路

  • 想法
    • 这道题本质上还是斐波那契数列,是动态规划,思考方式同样是分治法。
    • 可以从1列到4或者5,可以发现
  • 解法
    • 如何上第n节台阶,可以通过n-2节台阶走两步,或者通过n-1节台阶走一步。
    • F(n)=F(n-1)+F(n-2)
  • 做法
    • ans1来储存n-2台阶的方法数,ans2来存储n-1台阶的方法数

性能

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

53. 最大子数组和

代码

func maxSubArray(nums []int) int {
    ans := nums[0]
    tmp := nums[0]
    n := len(nums)

    i := 1
    for ; i<n; i++ {
        tmp = max(tmp+nums[i], nums[i])
        ans = max(ans, tmp)
    }
    return ans
}

func max (i, j int) int {
    if i>j {
        return i
    }
    return j
}

思路

  • 可以知道是道动态规划,重要的有两点:1.定义状态,定义子问题。2.状态转移方程
  • 1.定义子问题
    • dp[i]是以i为结尾的最大连续和
  • 2.状态转移方程
    • 选择dp[i-1]+nums[i]<nums[i]nums[i]大的那个
  • 3.事例[-2,1,-3,4,-1,2,1,-5,4]
    • 第一个,一定是-2
    • 第二个,选择-2+1或者1,一定是1
    • 第三个,选择1+(-3)或者-3,选择-2
    • 第四个,选择-2或者4,一定是4
    • 第五个,选择3或者-1,一定是3
    • 第六个,选择5或者2,一定是5

性能

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

322. 零钱兑换

代码

func coinChange(coins []int, amount int) int {
    ans := make([]int, amount+1)
    n:=len(coins)
    i := 1
    j := 0
    min := 10001

    for i<=amount {
        ans[i]=-1
        min = 10001
        for j=0; j<n; j++ {
            if i-coins[j]>=0 && ans[i-coins[j]]!=-1 {
                min = theMin(min, ans[i-coins[j]]+1)
            }
        }
        if min != 10001 {
            ans[i]=min
        }
        i++
    }
    return ans[amount]
}

func theMin(i, j int) int {
    if i<j {
        return i
    }
    return j
}

思路

  • 动态规划两个核心问题:1.子问题定义,2.状态转移方程
  • 子问题定义:
    • F(i)的含义,构成i元,最少需要多少枚硬币
  • 状态转移方程:
    • F(i)=Min(F(i-conis[0])+1, F(i-conis[1]+1, ......)
    • 简短解释:比i-conis[j]元最少的钱数+1。
  • 标准解释:
    • i是期望的钱数,j是每一个可以用的硬币
    • 如果i-conis[j]能被满足,那它的结果+1,就可以构造i元钱,就可能是构建i最少硬币数。
  • 贪心不能解决的测试case:
    • [3,7,11], 34

性能

518. 零钱兑换 II

代码

思路

性能

64. 最小路径和

代码

func minPathSum(grid [][]int) int {
    n := len(grid)
    m := len(grid[0])

    i:=0
    j:=0
    for i=1; i<n; i++ {
        grid[i][0]+=grid[i-1][0]
    }
    for i=1; i<m; i++ {
        grid[0][i]+=grid[0][i-1]
    }

    for i=1; i<n; i++ {
        for j=1; j<m; j++ {
            grid[i][j]+=min(grid[i-1][j], grid[i][j-1])
        }
    }
    return grid[n-1][m-1]
}

func min(i, j int) int {
    if i<j {
        return i
    }
    return j
}

思路

  • 到达每一位的最小值
  • 是上边的或左边的最小的,
  • 加自己。
  • 总结:grid[i][j]+=min(grid[i-1][j], grid[i][j-1])

性能

  • 时间复杂度:O(n*n)
  • 空间复杂度:O(1),直接修改原数组,额外只需要常数空间

72. 编辑距离

代码

思路

性能

121. 买卖股票的最佳时机

代码

func maxProfit(prices []int) int {
    theMin := prices[0]
    ans := 0

    for _, v := range prices {
        ans = max(ans, v-theMin)
        theMin = min(theMin, v)
    }
    return ans
}

func min(i, j int) int {
    if i<j {
        return i
    }
    return j
}
func max(i, j int) int {
    if i>j {
        return i
    }
    return j
}

思路

  • 当前最多挣的钱数 = 之前最大的 - 之前最小的,(之前最小的要在之前最大的之前)
  • 记录之前最多挣的钱数:ans,
  • 记录之前最小的那个值。
  • ans = max(ans, 当前的值-之前最小的值)

性能

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

122. 买卖股票的最佳时机 II

代码

思路

性能

123. 买卖股票的最佳时机 III

代码

思路

性能