算法与数据结构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))