这篇文章上次修改于 410 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
ACM程序课算法笔记3——动态规划
问题一:HDOJ2084——数塔问题
问题描述:
有一个数塔,从顶部出发,要求找到一条从顶部到底部的一条路径,使路径上的和最大
解题思路:
如果去掉左上方一条或者右上方一条,就会变成新的塔,如果知道这两个新的塔的结果,就可以获取题目的答案。
将一个问题分解成了两个子问题。
自顶而下的分析,自底而上的计算。
状态转移方程:
dp[i][j]
代表路径以i行第j个为起点的路径,路径的最大值(一开始储存了这个点的值)
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + dp[i][j]
问题二:HDOJ1176——免费馅饼
问题描述:
有 0-10个格子,每个时间点,都会有n个馅饼从天而降,会告诉你每个时间点n个煎饼的位置,假设人默认站在5位置开始,每一个时间都只能移动一格,求最大能捡到的煎饼数量
解题思路:
每个点都只有往下的三个选择,如果我先知道了往下的点能吃到的馅饼数量,就可以推出这个点
状态转移方程:
dp[j][i]
代表了在第i时间,站在j位置的收获的最多馅饼数量(一开始有馅饼为1没有为0)
dp[j][i] = max(dp[j][i + 1], dp[j + 1][i + 1], dp[j - 1][i + 1]) + dp[j][i];
问题三:最长有序子序列(LIS)
问题描述:
给一个长度为n的数字序列,求最长的递增子序列长度
解题思路:
先排除掉暴力的念头
所有的最长子序列最后一个元素的情况只有n中(n是原序列长度)
找到所有以每个数作为结尾的序列的长度中最长的长度储存,最前面的可以很简单的找到。而后面的可以根据前面的获得到。问题已解,时间复杂度O(n2)
状态转移方程:
dp[i]
代表了以第i个数作为结尾的递增子序列最长的长度。
dp[i] = max(dp[i],dp[j] + 1)
,其中j是0到i-1之间的任意一个数,且nums[j] < nums[i]
当然,也有复杂度为O(N log N)的优化版
问题四:HDOJ1160——Fat Mouse ‘s speed
问题描述:
输入很多老鼠的重量和速度。输出n个老鼠的数量,n行表示编号,该编号的老鼠必须重量是递增的,速度是递减的。
解题思路:
和问题三类似
状态转移方程:
dp[i]
代表了以第i个数作为结尾的递增子序列最长的长度。
dp[i] = max(dp[i],dp[j] + 1)
,其中j是0到i-1之间的任意一个数,且weight[j] < weight[i] and speed[j] > speed[i]
问题五:HDOJ1087——super jump
问题描述:
给你一个数字序列,找到一条递增子序列,其所有点的和最大
进阶版LIS
解题思路:
和问题三类似
状态转移方程:
dp[i]
代表了以第i个数作为结尾的递增子序列最大的路径值。初始为第 i 个数的值
dp[i] = max(dp[i],dp[j] + dp[i])
,其中 j 是 0 到 i - 1 之间的任意一个数,且nums[j] < nums[i]
问题六:最长公共子序列
问题描述:
给你两个字符串,找到它们最长的相同子序列
解题思路:
寻找这个问题的子问题,对于两个字符串的最长公共长度,通过判断最后的两个字符,我们可以找到方程一,如果不相同,可以找到方程2
状态转移方程:
如果字符串1的第m个和字符串2的第n个相同
dp[m][n] = dp[m - 1][n - 1] + 1
否则
dp[m][n] = max(dp[m][n - 1], dp[m -1][n])
问题七:HDOJ1421——搬寝室
问题描述:
提供n和k,给n个数字,找到k对的数,使这k对数的差的平方和最小
解题思路:
选择的k对数字,每对数字都是大小排序后相邻的。前两个数和前四个数的选择方式
取一对
两个数,一种情况
三个数,有第三个和无第三个
四个数,有第四个和(三个数的情况)
n个数,有第n个和(n - 1个数的情况)
取k对
n个数取k对,分为 有第n个数(n - 2个数取 k - 1对 + 这对) 和 无第n个的(n - 1个数取k对)
状态转移方程:
dp[i][j]
代表前 i 个数字取 j 对,最小的差的平方和
dp[i][j] = min(dp[i - 1][j], dp[i - 2][j - 1] + S)
S是最小的两个数字的差的平方和。
问题八:HDOJ1058——Humble Numbers
问题描述:
质数因子只有2、3、5或7的数称为谦卑数。序列1、2、3、4、5、6、7、8、9、10、12、14、15、16、18、20、21、24、25、27这是前20个谦卑数。
要求输入n,输出第n个谦卑数。
算法思路:
每个大的数都是由在数列里面小的数乘上 2, 3, 5,7
所以得到以下状态转移方程
dp[n] = min(dp[a1] * 2, dp[a2] * 3, dp[a3] * 5, dp[a4] * 7)
a1, a2, a3, a4分别代表了四个因数和其最小数(此最小数乘以这个因数要大于dp[n - 1]
)