本文仅供个人学习使用,免费分享。每日更新,建议关注收藏!
目前[机试指南]本系列已经达到字数10w+,所以按原每个章节拆开发布,点击目录跳转。
本站友情链接:
- c/c++算法题指南
严书代码
c/c++大复习1
c/c++大复习2 - python算法题指南
牛客华为机试103精华
python输入输出大全
python语法
PAT甲级真题刷题笔记 共179道 - python官方文档
python官方文档 - 机试指南系列
基础篇
贪心篇
递归分治搜索篇
数据结构进阶篇(树/图/优先队列)
数学问题篇
动态规划篇
STL篇
目录
动态规划
基本思想同分治法,也是将问题分解成若干子问题,先求解子问题;
动态规划处理的问题是每个问题都是离散的而且不依赖其他子问题;若用分治法,则子问题数目太多,存在大量重复计算;而动态规划会把子问题答案保存下来备用,减少大量重复计算。
如fibonacci数列求解,其中有大量的重复计算,如fibonacci(7)要计算fibonacci(6)、fibonacci(5),fibonacci(6)也要计算fibonacci(5),为避免重复计算,设置数组dp[n]存放fibonacci(n),
dp3跳台阶扩展问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。
f(n)=f(n-1)+f(n-2)+…+f(2)+f(1) (i)
f(n-1)=f(n-2)+…+f(1) (ii)
(i)&(ii)式得到f(n)=2f(n-1)
可以发现是等比数列,可得 n=1时f(n+1)=1,n>1即n>=2时f(n+1)=2f(n)
dp4:最小花费爬楼梯
给定一个整数数组 cost ,其中 cost[i] 是从楼梯第i 个台阶向上爬需要支付的费用,下标从0开始。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。
#递归超时版
def go(cost,start,hadcost):
end=length-1
if(start+1==end):
return hadcost+cost[start]
elif(start==end):
return hadcost+cost[end]
else:
return min(go(cost,start+1,hadcost+cost[start]),go(cost,start+2,hadcost+cost[start]))
length=int(input())
cost=list(map(int,input().split()))
x=min(go(cost,0,0),go(cost,1,0))
print(x)
#for循环 dp打表超省时
slong=int(input())
s=input().split()
s=[int(i) for i in s]
dp=[0,0]
for i in range(2,slong+1):
dp.append(min(dp[i-1]+s[i-1],dp[i-2]+s[i-2]))
print(dp[slong])
dp26:跳跃游戏(本人博客文章中华为机试那篇的补充真题的变形)
给定一个非负整数数组 nums ,你最初位于数组的第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
#数目过大时通过不了的代码 动态规划
import sys
for line in sys.stdin:
a = line
jump=[int(x) for x in input().split()]
#print(jump)#下标i表示当前位置 jump[i]代表能跳的最大长度
location=len(jump)-1
dp=[]
cnt=0
i=0
now=0
while(now<len(jump) ):
if(now==len(jump)-1):
print('true')
exit()
for j in range(jump[i]+1):
if((j+now) not in dp):
dp.append(j+now)
#print(dp,now)
cnt+=1
if(cnt>=len(dp)):break
now=dp[cnt]
i=now
print('false')
'''
解决方法
采用贪心策略,从后向前遍历数组,看当前位置 + 值 是否 >= 给定的位置,最终判断是否为 0
'''
from typing import (
List,
)
class Solution:
"""
@param a: A list of integers
@return: A boolean
"""
def can_jump(self, a: List[int]) -> bool:
# write your code here
i=0
max_i=0
if(len(a)<=1):return 'true'
while(i<len(a)-1):
if(a[i]>0):
max_i=max(max_i,i+a[i])
i+=1
elif(max_i>i):
i+=1
else:return 'false'
print(i,max_i)
if(i>=len(a)-1):return 'true'
www.lintcode.com/problem/114
1.数学题解法
机器人从左上角走到右下角,需要向下走m - 1步,向右走n - 1步,那么总步数也是一定的,为m + n - 2步。问题就转化成,从m + n - 2步中选出m - 1步向下,其余步数自然是向右,有多少种组合?即C(m-1,m+n-2)
2.动态规划打表 及优化
dp[i][j]=dp[i−1][j]+dp[i][j−1] 优化成dp[j]+=dp[j-1]
为什么能这样优化呢?因为dp[i][j]=dp[i−1][j]+dp[i][j−1] 等价于dp[i][j]+=dp[i][j−1] ,这样看 多出来的行坐标就不需要使用了
class Solution:
"""
@param m: positive integer (1 <= m <= 100)
@param n: positive integer (1 <= n <= 100)
@return: An integer
"""
def unique_paths(self, m: int, n: int) -> int:
# write your code here
dp=[0]*(m*n)
#dp[i][j]表示走到i,j的路径数 dp[i][j]=dp[i-1][j]+dp[i][j-1]
#优化成dp[j]+=dp[j-1] 当第i次遍历到dp[j]时,dp[j]表示到达(i, j)最多的路径数。
j=0
dp[j]=1
for i in range(m):
for j in range(n):
dp[j]+=dp[j-1]
print(dp)
print(dp)
return dp[n-1]
trick :双向dp
https://www.lintcode.com/problem/534/
由于是环形结构,所以要交叉的双向更新。
class Solution:
# @param nums: A list of non-negative integers.
# return: an integer
def houseRobber2(self, nums):
# write your code here
n = len(nums)
if n == 0:
return 0
if n == 1:
return nums[0]
dp = [0] * n
dp[0], dp[1] = 0, nums[1] #舍去nums[0],则可以一直遍历到nums[-1]
for i in range(2, n):
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
answer = dp[n - 1]
dp[0], dp[1] = nums[0], max(nums[0], nums[1])#不舍去nums[0],则可以一直遍历到nums[-2]
for i in range(2, n - 1):
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
return max(dp[n - 2], answer)
递归求解
例题12.1 N阶楼梯上楼问题(华中科技大学复试上机题) http://t.cn/Aij9Fr3V
import sys
for line in sys.stdin:
a = int(line)
dp=[0 for i in range(a+1)]
dp[0]=1
for i in range(a+1):
if(i<a):
dp[i+1]+=dp[i]
if(i+1<a):
dp[i+2]+=dp[i]
print(dp[-1])
习题12.1 吃糖果(北京大学复试上机题) http://t.cn/AiQsVyKz
import sys
for line in sys.stdin:
c = int(line)
dp=[0 for i in range(c+1)]
dp[0]=1
dp[1]=1 #dp[i]表示有i块的方案数
if(c>=2):
for i in range(2,c+1):
dp[i]+=dp[i-1]
dp[i]+=dp[i-2]
#print(i,dp[i])
print(dp[-1])
最大连续子序列乘积product
线性dp。由于数据中有正有负,所以我们利用两个dp数组来完成。用f[i]来保存计算到第i个时的最大值,用g[i]来保持计算到第i个时的最小值。
nums[i]的最大值f[i]=max(max(f[i-1]×nums[i], g[i-1]×nums[i]), nums[i])。g[i]=min(min(f[i-1]*×[i], g[i-1]×nums[i]), nums[i])。
这两串公式已经合并了两种情况,无非是一大一小或者两个相等:
- 继续连着乘 nums[i]
- 截断,从nums[I]起始
最后由于我们要求的是最大值,直接对f数组取最大值即可。
优化:计算到第i个时的最大值和计算到第i个时的最小值都用一个变量来表示和更新
class Solution:
"""
@param nums: An array of integers
@return: An integer
"""
def maxProduct(self, nums):
if not nums:
return None
global_max = prev_max = prev_min = nums[0]
for num in nums[1:]:
if num > 0:
curt_max = max(num, prev_max * num) #max*num
curt_min = min(num, prev_min * num)
else:
curt_max = max(num, prev_min * num) #min*num
curt_min = min(num, prev_max * num)
print(prev_max,prev_min,curt_max,curt_min)
global_max = max(global_max, curt_max)
prev_max, prev_min = curt_max, curt_min
return global_max
双数组
https://www.lintcode.com/problem/1310/
在不允许用除法以及时间复杂度必须o(n)情况下,如何解?
不允许用除法也避免了数组中有0的情况
解决方法:利用索引左侧所有数字的乘积和右侧所有数字的乘积(即前缀与后缀)相乘得到答案。对于给定索引 i,我们将使用它左边所有数字的乘积乘以右边所有数字的乘积。故建立左、右乘积两个数组
这两个数组的建立思想也与动态规划【dp双数组】类似。
举例:比如左乘积数组left,left[I]代表nums[I]左侧所有元素的乘积,left[i]=left[i-1]*nums[I-1];右侧同理。
right[I]代表nums[I]右侧所有元素的乘积
class Solution:
"""
@param nums: an array of integers
@return: the product of all the elements of nums except nums[i].
"""
def product_except_self(self, nums: List[int]) -> List[int]:
# write your code here
left,right,answer=[1]*len(nums),[1]*len(nums),[1]*len(nums)
for i in range(len(nums)):
if(i>0):
left[i]=left[i-1]*nums[i-1