算法笔记--02动态规划

动态规划算法

动态规划算法思想概括

自底向上的递推算法,通过求解子问题,逐步得到原问题的解。

状态转移方程

状态定义
状态是指问题的一个特定阶段的解的情况。比如,对于背包问题,状态就是当前背包的容量和已经放入背包的物品。
状态转移
确定如何从一个状态转移到另一个状态。比如,在背包问题中,对于第 i 个物品,可以选择放入背包或不放入背包,然后根据这两种选择的结果来决定最终的状态值。

算法框架

  1. 定义状态
  2. 确定状态转移方程
  3. 确定初始条件和边界条件
  4. 确定执行顺序
  5. 返回最终结果

经典题目

斐波那契数列

后一个数等于前两个数的和。

func fib(n int) {
	if n <= 1 {
		fmt.Println(n)
	}

	dp := make([]int, n+1)

	dp[0] = 0
	dp[1] = 1

	for i := 2; i <= n; i++ {
		dp[i] = dp[i-1] + dp[i-2]
		fmt.Println(dp[i])
	}

	fmt.Println(dp[n])
}

func main() {
	fib(10)
}

爬楼梯

每次可以爬 1 或 2 个台阶,求爬到第 n 阶楼梯的方法数。

func climb(n int) {
    if n <= 2 {
        fmt.Println(n)
    }

    dp := make([]int, n+1)
    dp[1] = 1
    dp[2] = 2

    for i := 3; i <= n; i++ {
        dp[i] = dp[i-1] + dp[i-2]
    }

    fmt.Println(dp[n])
}
func main(){
    climb(10)
}

01背包问题

有 n 个物品,每个物品只有一个。
每个的重量为 w[i],价值为 v[i],背包的容量为 W。求在不超过背包容量的情况下,背包中物品的最大总价值。
|

func main() {
	weights := []int{1, 2, 3, 4}
	values := []int{10, 20, 60, 40}
	W := 5

	maxValue := knapsack(weights, values, W)
	fmt.Printf("The maximum value is: %d\n", maxValue)
}

/*
  容量 0 1 2 3 4 5
物品0 [0 0 0 0 0 0]
物品1 [0 0 0 0 0 0]
物品2 [0 0 0 0 0 0]
物品3 [0 0 0 0 0 0]
物品4 [0 0 0 0 0 0]
*/

func knapsack(w []int, v []int, W int) int {
	// 定义dp数组,dp[i][j]表示前i个物品放入容量为j的背包中的最大价值
	dp := make([][]int, len(w)+1)
	for i := range dp {
		dp[i] = make([]int, W+1)
	}
	// 遍历物品
	for i := 1; i <= len(w); i++ {
		// 遍历背包容量
		for j := 0; j <= W; j++ {
			// 记当前物品在原有数组中的下标
			index := i - 1
			if j < w[index] {
				// 当前物品重量大于此时背包容量,不能放入背包
				dp[i][j] = dp[i-1][j]
			} else {
				// 当前物品重量小于等于背包容量,可以选择放入或不放入背包
                // 因为当前物品只有一个,只能从前面i-1个物品中取。
				dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[index]]+v[index])
			}
		}
	}
	return dp[len(w)][W]
}

完全背包问题

有 n 个物品,每个物品有无限个。
每个的重量为 w[i],价值为 v[i],背包的容量为 W。求在不超过背包容量的情况下,背包中物品的最大总价值。

与01背包问题相区别的是状态转移方程:
01背包:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[index]] + v[index])
完全背包:dp[i][j] = max(dp[i-1][j], dp[i][j-w[index]] + v[index])

最长公共子列

给定两个序列 X 和 Y,求 X 和 Y 的最长公共子列的长度。

func main() {
	X := []int{1, 2, 3, 4, 5}
	Y := []int{2, 4, 6, 8, 10}

	lcs := longestCommonSubsequence(X, Y)
	fmt.Printf("The length of the longest common subsequence is: %d\n", lcs)
}

func longestCommonSubsequence(X []int, Y []int) int {
	m, n := len(X), len(Y)
	dp := make([][]int, m+1)
	for i := range dp {
		dp[i] = make([]int, n+1)
	}

	for i := 1; i <= m; i++ {
		for j := 1; j <= n; j++ {
			if X[i-1] == Y[j-1] {
				dp[i][j] = dp[i-1][j-1] + 1
			} else {
				dp[i][j] = max(dp[i-1][j], dp[i][j-1])
			}
		}
	}

	return dp[m][n]
}

最小路径和

给定一个 m x n 的矩阵,从左上角到右下角的最小路径和,每次只能向右或向下移动。

func main() {
	matrix := [][]int{
		{1, 3, 1},
		{1, 5, 1},
		{4, 2, 1},
	}

	minPathSum := minPathSum(matrix)
	fmt.Printf("The minimum path sum is: %d\n", minPathSum)
}

func minPathSum(matrix [][]int) int {
	m, n := len(matrix), len(matrix[0])
	dp := make([][]int, m)
	for i := range dp {
		dp[i] = make([]int, n)
	}

    // 初始化第一行和第一列
	dp[0][0] = matrix[0][0]
	for i := 1; i < m; i++ {
		dp[i][0] = dp[i-1][0] + matrix[i][0]
	}
	for j := 1; j < n; j++ {
		dp[0][j] = dp[0][j-1] + matrix[0][j]
	}
	for i := 1; i < m; i++ {
		for j := 1; j < n; j++ {
			dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + matrix[i][j]
		}
	}

	return dp[m-1][n-1]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值