【算法题】机试指南-动态规划篇

本文仅供个人学习使用,免费分享。每日更新,建议关注收藏!
目前[机试指南]本系列已经达到字数10w+,所以按原每个章节拆开发布,点击目录跳转。

本站友情链接:

  1. c/c++算法题指南
    严书代码
    c/c++大复习1
    c/c++大复习2
  2. python算法题指南
    牛客华为机试103精华
    python输入输出大全
    python语法
    PAT甲级真题刷题笔记 共179道
  3. python官方文档
    python官方文档
  4. 机试指南系列
    基础篇
    贪心篇
    递归分治搜索篇
    数据结构进阶篇(树/图/优先队列)
    数学问题篇
    动态规划篇
    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])。
这两串公式已经合并了两种情况,无非是一大一小或者两个相等:

  1. 继续连着乘 nums[i]
  2. 截断,从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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七灵微

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值