算法与数据结构10-动态规划

动态规划:

  一维动态规划:

    目前我们所见到的DP问题都是1D的,让我们看一下其他例子:

       问题描述1:给定n,找到不同的n写成1,3,4相加的方法

  例如:n=5,答案是6

  答案:公式:Dn = Dn-1 +Dn-3 +Dn-4

     D[0] = D[1]=D[2] D[3]=2

     for (i=4;i<=n;i++){

       D[i] = D[i-1]+D[i-3]+D[i-5]

     }

  问题描述2:

    假设你是一个职业拿钱犯,你打算拿一个街道的钱。每个房子里有定数量的钱。限定你的唯一条件是相邻的房子安保系统是相连的,如果拿相邻的房子就会惊动警察。

    给定一个非负整数的列表代表每个房子当中的钱,计算在不惊动警察的情况下你可以拿到最多的钱。

  答案:定义公式Yes(i) = 当前抢这个房子得到的最大值 No(i)=当前不抢这个房子得到的最大值

    得到规律: Yes(i) = No(i-1)+a[i]

         No[i] =max(Yes[i-1], No[i-1])

         Yes[1] = a[1] Yes[2] = No[1]+a[2]

         No[1] = 0 No[2] = max(Yes[1],No[1])

         result: max(Yes[i], No[i])

def job(nums):
    n = len(nums)
    dp=[ [0]*2 for _ in range(n+1)]
    for i in range(1, n+1):
        dp[i][0] = max(dp[i-1][0], dp[i-1][1])
        dp[i][1] = dp[i-1][0]+nums[i-1]
    return max(dp[n][0], dp[n][1])

def job1(nums):
    yes, no = 0, 0
    for i in nums:
        yes, no = i+no, max(yes, no)
    return max(no, yes)
nums=[2,7,8,3,1]
print(job1(nums))

  问题描述3:

    一个公司领导和员工为树状结构排列,一个人去公司拿钱,拿钱的规则是拿了一个人的钱,那么这个人的领导和下属就不能再拿了,一共能拿多少钱?

    Yes[i] = 所有下属的和No[i-1] + a[i]

    No[i] = 所有的和max(Y[i-1], No[i-1]) 

  问题描述4:

    假定给定一块n*2的地板,瓷砖的大小为1*2,计算需要一共有铺满长廊的方式。瓷砖可以水平放置:1*2.也可以数值2*1.

    F[n] = F[n-1] + F[n-2]

  问题描述5:

    有一个楼梯,每次可以走1层或者2层,cost数组表示每一层所需要花费的值。可以从0节或者1节台阶开始走,一旦交了过路费,可以上1个台阶或者两个台阶,计算花费最少费用登顶。

    dp[i]代表到达第I层所需要的花费

    dp[i] = min(dp[i-2] + cost[i-2], dp[i-1]+cost[i-1])

def minCostClimbing(cost):
    n = len(cost) + 1
    dp = [0] * n
    for i in range(2, n):
        dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])
    return dp[n-1]

nums = [1,100,1,1,1,100,1,1,100,1]
print(minCostClimbing(nums))

  问题描述6:

    如果一个编码1-A,2-B,..., 26-Z,问给一个字符串,有几种解码形式。

def numDecoder(str):
    if str == "" or str[0]=="0": return 0
    dp =[1, 1]
    for i in range(2, len(str)+1):
        result = 0
        if 10 <= int(str[i-2:i]) <=26:
            result = dp[i-2]
        if str[i-1] != "0":
            result += dp[i-1]
        dp.append(result)
    return dp[len(str)]

str = "120"
print(numDecoder(str))

  问题描述7:

    给定n:用1.。。n个数字来表达Binary Search Tree,问有多少种表达方式

      公式为从1到n求(Ti-1*Tn-i)的和,就是出名的CatalonNumber

def NumTree(num):
    if num < 2:
        return num
    sol = [0] * (num + 1)
    sol[0] = sol[1] = 1
    for i in range(2, num + 1):
        for left in range(0, i):
            sol[i] += sol[left] + sol[i-1-left]
    return sol[num]
print([NumTree(i) for i in range(1,10)])

  问题描述8:

    给定一个数组,寻找连续子数组的元素乘积最大:

def maxProduct(nums):
    if len(nums)==0:
        return 0
    maximum = minimum = result = nums[0]
    for i in range(1, len(nums)):
        maximum, minimum = max(maximum * nums[i], minimum * nums[i], nums[i]),\
                           min(maximum * nums[i], minimum * nums[i], nums[i])
        result = max(result, maximum)
    return result
nums = [2, 5, 6, -2, 8, 3]
print(maxProduct(nums))

   问题描述9:

    给定一个数组,表示每天的股票价格,可以进行一次交易(先买后卖),问如何得到最大利润。

def maxProfit(prices):
    if len(prices) < 2:
        return 0
    minPrice = prices[0]
    dp = [0]*len(prices)
    for i in range(len(prices)):
        dp[i] = max(dp[i-1], prices[i]-minPrice)
        minPrice = min(minPrice, prices[i])
    return dp[len(prices)-1]

prices = [7,1,5,3,6,4]
print(maxProfit(prices))

    给定一个数组,表示每天的股票价格,你可以进行任意多次的交易(先买再卖,在再次买入的时候必须将之前的股票卖出,如何能得到最大利润)

def maxProfit2(prices):
    max_profit = 0
    for i in range(1, len(prices)):
        max_profit += max(0, prices[i]-prices[i-1])
    return max_profit

    给定一个数组,表示每天的股票价格,你可以进行任意多次的交易(先买再卖,在再次买入的时候必须将之前的股票卖出),每次交易需要缴纳手续费K元,如何能得到最大利润。

def maxProfit3(prices, k):
    if len(prices) < 2:
        return 0
    cash , hold = 0, -prices[0]
    for i in range(1, len(prices)):
        cash, hold = max(cash, hold + prices[i] - k), max(hold, cash - prices[i])
    return cash
prices = [1,3,2,8,4,9]
k = 2
print(maxProfit3(prices,k))

  给定一个数组,表示每天股票价格,你可以进行两次的交易(先买再卖,在再次买入的时候必须将之前的股票卖出),问如何能得到最大的利润

def maxProfit4(prices):
    if len(prices) < 2:
        return 0
    total_max_profit = 0
    n = len(prices)
    left_profit = [0] * n
    min_price = float('inf')
    for i in range(n):
        min_price = min(min_price, prices[i])
        total_max_profit = max(total_max_profit, prices[i]-min_price)
        left_profit[i] = total_max_profit
    max_profit = 0
    max_price = float('-inf')
    for i in range(n-1, 0, -1):
        max_price = max(max_price, prices[i])
        max_profit = max(max_profit, max_price - prices[i])
        total_max_profit = max(total_max_profit, max_profit + left_profit[i-1])
    return total_max_profit

  给定一个数组,表示每天股票价格,你可以进行K次的交易(先买再卖,在再次买入的时候必须将之前的股票卖出),问如何能得到最大的利润。

def maxProfit5(prices, k):
    if len(prices) < 2:
        return 0
    if len(prices) <= k/2:
        maxProfit(prices)
    local = [0] * len(prices)
    globl = [0] * len(prices)
    for i in range(1, len(prices)):
        diff = prices[i] - prices[i-1]
        j = k
        while j > 0:
            local[j] = max(globl[j-1]+max(diff, 0), local[j]+diff)
            globl[j] = max(globl[j], local[j])
            j -= 1
    return globl[k]

prices = [3, 2, 6, 5, 0, 3]
k = 2
print(maxProfit5(prices, k))

  给定一个数组,表示每天的股票价格,你可以进行任意多次的交易(先买再卖,在再次买入的时候必须将之前的股票卖出),但卖过之后需要休息一天(卖掉股票之后的一天不允许买入),问如何能得到最大利润。

def maxProfit6(prices, k):
    if len(prices) < 2:
        return 0
    if len(prices) <= k/2:
        return maxProfit(prices)
    n = len(prices)
    sell = [0] * n
    buy = [0] * n
    sell[0] = 0
    buy[0] = -prices[0]
    for i in range(1, n):
        sell[i] = max(sell[i-1], buy[i-1]+ prices[i])
        buy[i] = max(buy[i-1], (sell[i-2] if i > 1 else 0)- prices[i])
    return sell[-1]

二维动态规划:

  1. 独特的路径:

    一个机器人被放置在棋盘的左上角,棋盘上有m*n个格子,机器人只可以往下移动或往右移动。机器人的目标是达到右下角,可以有多少种不重复的路径。

def UniquePaths(m ,n):
    aux = [[1 for x in range(n)] for x in range(m)]
    for i in range(1, m):
        for j in range(1, n):
            aux[i][j] = aux[i][j-1] + aux[i-1][j]
    return aux[-1][-1]
print(UniquePaths(4,3))

  2. 在棋盘上移动:

    给定一个有n行n列的棋盘。每个格子上有不同的值代表该格子的价值。目的是找到获利最大的移动路线(从第一行的某个格子到最后一行的某个格子)

    移动方法:

    1. 移动到下一行的之前一列(up then left)

    2. 移动到下一行(up)

    3. 移动到下一行的下一列

      dp(i, j) = max(a[i-1, j-1], a[i-1, j], a[i-1, j+1]) + a[i, j]

def ChessMove(board):
    result = board
    m = len(board)
    n = len(board[0])
    for i in range(1, m):
        for j in range(1, n):
            result[i][j] = max(0 if j == 0 else result[i-1][j-1], result[i-1][j], 0 if j == n-1 else result[i-1][j+1])+\
                           board[i][j]
    return max(result[-1])

board = [[3, -2, 6, -3, 4, 1, 2],
         [0, 4, 1, 3, -1, 4, 3],
         [2, 2, -1, 3, 2, 0, 2]]
print(ChessMove(board))

  3. 0/1背包问题:

    背一个背包闯入一家珠宝店,店里有林林总总的珠宝,个不重样,每一个珠宝有自己的重量和价值。但是书包有承载上限,如何包内物品的重量不超过限定值,价值最大。

    思路:

      就是每一个商品拿与不拿  比如第i个商品,重量为w, 如果拿该商品,则前面需要 T(i,j) = T(i-1, j-w)+value ,如果不拿该商品,则前面需要T(i, j) = T(i-1, j),这样T(i, j)= max(T(i-1, j), T(i-1, j-w)+value)

def knapSack(w, wt, val, n):
    K = [[0 for x in range(w+1)]for x in range(n+1)]
    for i in range(1,n+1):
        for j in range(w+1):
            if w == 0:
                K[i][j] = 0
            elif wt[i-1] <= j:
                K[i][j] = max(K[i-1][j], K[i-1][j-wt[i-1]]+ val[i-1])
            else:
                K[i][j] = K[i-1][j]
    return K[n][w]

val = [5, 7, 10, 13, 3, 11]
wt = [2, 3, 4, 6, 1, 5]
w = 14
n = len(val)
print(knapSack(w, wt, val, n))

  4. 最长公共子序列

    给定一个序列:X = x1,x2,x3,x4 Y = y1,y2,y3,y4,最大公共子序列必须满足其同时是X和Y的子序列,找到最大公共子序列的长度。

def LCS(X, Y, m, n):
    matrix = [[ 0 for x in range(n+1)] for l in range(m+1)]
    result = 0
    for i in range(m+1):
        for j in range(n+1):
            if (i == 0 or j == 0):
                matrix[i][j] = 0
            elif(X[i-1] == Y[j-1]):
                matrix[i][j] = matrix[i-1][j-1] + 1
                result = max(result, matrix[i][j])
            else:
                matrix[i][j] = max(matrix[i-1][j], matrix[i][j-1])
    return result

X = 'AGGTAB'
Y = 'GXTXAYB'
m = len(X)
n = len(Y)
print(LCS(X, Y, m, n))

  5. 给定一个长度为N的数组,找到一个最长的单调自增子序列(LIS)(不一定连续,但是顺序不能乱)。例如给定一个数组:10 22 9 33 21 50 41 60 80,则最长的单调递增子序列为10 22 33 50 60 80,长度为6 

from bisect import bisect
def lengthOfLIS(nums):
    temp = []
    for num in nums:
        pos = bisect(temp, num)
        if pos >= len(temp):
            temp.append(num)
        else:
            temp[pos] = num
    return len(temp)
nums = [10, 9, 2, 5, 3, 7, 101, 18]
print(lengthOfLIS(nums))

    

 

posted @ 2019-12-10 22:27  吕晓宁  阅读(269)  评论(0编辑  收藏  举报