动态规划 LeetCode汇总

动态规划算法题汇总

Leetcode

牛客网

877 Stone Game

Alex and Lee play a game with piles of stones. There are an even number of piles arranged in a row, and each pile has a positive integer number of stones piles[i].

The objective of the game is to end with the most stones. The total number of stones is odd, so there are no ties.

Alex and Lee take turns, with Alex starting first. Each turn, a player takes the entire pile of stones from either the beginning or the end of the row. This continues until there are no more piles left, at which point the person with the most stones wins.

Assuming Alex and Lee play optimally, return True if and only if Alex wins the game.

Example 1:

Input: [5,3,4,5]
Output: true

solution
Alex的得分取决于Lee选择后最大的那个

firstmax(i,j) ==第i个数到第j个数的选择

class Solution:
    def stoneGame(self, p: List[int]) -> bool:
        cache = {}
        def firstmax(i,j):
            if i >= j: return 0
            if j==i and j<len(p): return p[i]
            if (i,j) in cache: return cache[i,j]
            
            res = max(p[i]+min(firstmax(i+1,j-1),firstmax(i+2,j)),p[j]+min(firstmax(i,j-2),firstmax(i+1,j-1)) )
            
            cache[(i,j)] = res 
            print (cache)
            return res
        
        A = firstmax(0,len(p)-1)
        L = sum(p) - A
        
        return A > L
931Minimum Falling Path Sum

Given a square array of integers A, we want the minimum sum of a falling path through A.

A falling path starts at any element in the first row, and chooses one element from each row. The next row’s choice must be in a column that is different from the previous row’s column by at most one.

Example 1:

Input: [[1,2,3],[4,5,6],[7,8,9]]
Output: 12
Explanation:
The possible falling paths are:
[1,4,7], [1,4,8], [1,5,7], [1,5,8], [1,5,9]
[2,4,7], [2,4,8], [2,5,7], [2,5,8], [2,5,9], [2,6,8], [2,6,9]
[3,5,7], [3,5,8], [3,5,9], [3,6,8], [3,6,9]
The falling path with the smallest sum is [1,4,7], so the answer is 12.

solution

class Solution:
    def minFallingPathSum(self, A: List[List[int]]) -> int:
        n = len(A)
        dp = [[0]*(n+2) for i in range(n)]
        
        for i in range(n):
            for j,x in enumerate(A[i]):
                dp[i][j+1] = x
        
        for i in range(n):
            dp[i][0] = 1000
            dp[i][n+1] = 1000
        
        for i in range(1,n):
            for j in range(1,n+1):
                dp[i][j] = min(dp[i-1][j-1]+dp[i][j],dp[i-1][j]+dp[i][j],dp[i-1][j+1]+dp[i][j])
                
        return(min(dp[n-1]))
983 Minimum Cost For Tickets
In a country popular for train travel, you have planned some train travelling one year in advance. The days of the year that you will travel is given as an array days. Each day is an integer from 1 to 365.

Train tickets are sold in 3 different ways:

a 1-day pass is sold for costs[0] dollars;
a 7-day pass is sold for costs[1] dollars;
a 30-day pass is sold for costs[2] dollars.

The passes allow that many days of consecutive travel. For example, if we get a 7-day pass on day 2, then we can travel for 7 days: day 2, 3, 4, 5, 6, 7, and 8.

Return the minimum number of dollars you need to travel every day in the given list of days.

Example 1:
Input: days = [1,4,6,7,8,20], costs = [2,7,15]
Output: 11

Example 2:
Input: days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15]
Output: 17

solution
创建一个长度为days[-1]+1的dp日历,记录价格

如果当天不是旅行日,dp[i]=dp[i-1]

如果是旅行日,当天的价格取决于1天前+日票、7日前+周票、30天前+月票的价格,但要考虑是否满足过去有7日or30日

class Solution:
    def mincostTickets(self, days: List[int], costs: List[int]) -> int:
        dp = [0 for i in range(days[-1]+1)]
        
        for i in range(days[-1]+1):
            if i not in days: 
                dp[i] = dp[i-1]
            else:
                dp[i] = min(dp[i-1]+costs[0],dp[max(i-7,0)]+costs[1],dp[max(i-30,0)]+costs[2])
        return dp[-1]
322 Coin Change
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
Example 2:

Input: coins = [2], amount = 3
Output: -1

solution

完全背包问题
背包大小V:amount

每件商品的价值W :coins

每个商品的代价:1

每件商品可以拿0/1/2…

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float("inf") for i in range(amount+1)]
        dp[0] = 0
       
        for coin in coins:
            for i in range(coin,amount+1):
                dp[i] = min(dp[i], dp[i-coin]+1)
        print(dp)
        if dp[-1] == float('inf'):
            return -1
        else:
            return dp[-1]
        
1105 Filling Bookcase Shelves

给n本书,每本书有一个厚度和高度,摆在书架上。
书架每一层最大宽度是shelf_width,摆不下时可以摆在下一层。
每层的高度为当前层所有书的最大高度,问书的总高度最小是多少。

Input: books = [[1,1],[2,3],[2,3],[1,1],[1,1],[1,1],[1,2]], shelf_width = 4
Output: 6
Explanation:
The sum of the heights of the 3 shelves are 1 + 3 + 2 = 6.
Notice that book number 2 does not have to be on the first shelf.

solution

对于第i+1本书有两种可能
1.独自成为一层 dp[i+1] = dp[i] + h[i+1]
2.与前面的书一起成为一层,要求,当前层的width<shelf_width
dp[i+1] = dp[j]+max(h[j+1]~~h[i+1])
即第j+1本书到i+1本书为一层

class Solution:
    def minHeightShelves(self, books: List[List[int]], shelf_width: int) -> int:
        n = len(books)
        dp = [float('inf') for i in range(n+1)]
        dp[0] = 0
        
        for i in range(1,n+1):
            cur_w = books[i-1][0]
            cur_h = books[i-1][1]
            dp[i] = dp[i-1] + cur_h
            
            for j in range(i-1,0,-1):
                cur_w +=  books[j-1][0]
                cur_h = max(cur_h,books[j-1][1])
                if(cur_w > shelf_width):
                    break
                dp[i] = min(dp[i],dp[j-1]+cur_h)
                
        return dp[-1]
714 Best Time to Buy and Sell Stock with Transaction Fee
Your are given an array of integers prices, for which the i-th element is the price of a given stock on day i; and a non-negative integer fee representing a transaction fee.

You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)

Return the maximum profit you can make.

Example 1:
Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit can be achieved by:
Buying at prices[0] = 1
Selling at prices[3] = 8
Buying at prices[4] = 4
Selling at prices[5] = 9
The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

solution

股票交易问题一种方法是计算两天之间的价格差值,此题可用贪心算法,只要当天的价格卖出后减去手续费仍大于零,我就卖出。但是注意minimum的取值,不能取为当天的价格,而是当天的价格-fee

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        n = len(prices)
        minimum = prices[0]
        res = 0
        for i in range(1,n):
            if prices[i] < minimum:
                minimum = prices[i]
                
            elif prices[i]-minimum-fee > 0:
                res += prices[i]-minimum-fee
                minimum = prices[i] - fee
                
        return res

另一种方法是使用dp,搞清楚初始状态 转移方程
buy[i]:buy状态下的最大利润
sell[i]:sell状态下的最大利润

buy状态:第i天 1.买(处于sell状态,卖了才能买)2.不变
sell状态:第i天 1.卖(处于buy状态,买了才能卖)2.不变
buy的初始状态:-prices[0]-fee
sell的初始状态:0

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        n = len(prices)
        buy = [0]*n
        sell = [0]*n
        buy[0] = -prices[0] - fee
        
        for i in range(1,n):
            buy[i] = max(buy[i-1],sell[i-1]-prices[i]-fee)
            sell[i] = max(sell[i-1],buy[i-1]+prices[i])
            
        return sell[-1]
300. Longest Increasing Subsequence

Given an unsorted array of integers, find the length of longest increasing subsequence.

Example:

Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.

solution
LIS 最长上升子串 转移方程:当第i个数做判断,如果nums[i]不满足条件,dp[i] = dp[i-1]

else: dp[i] = dp[i-1]+1
时间复杂度O(n²)

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [1]*n 
        
        if n==0:
            return 0
        if n==1:
            return 1
        
        for i in range(n):
            for j in range(0,i):
                if nums[j] < nums[i]:
                    dp[i] = max(dp[i],dp[j]+1)
        return max(dp)
            
646. Maximum Length of Pair Chain

You are given n pairs of numbers. In every pair, the first number is always smaller than the second number.

Now, we define a pair (c, d) can follow another pair (a, b) if and only if b < c. Chain of pairs can be formed in this fashion.

Given a set of pairs, find the length longest chain which can be formed. You needn’t use up all the given pairs. You can select pairs in any order.

Example 1:
Input: [[1,2], [2,3], [3,4]]
Output: 2
Explanation: The longest chain is [1,2] -> [3,4]

solution
首先要保证pairs有序

然后转移方程:针对当前第i个,他后面的j可能比他小,则dp[j]不变,或者比他大,则dp[j] = dp[i]+1

class Solution:
    def findLongestChain(self, pairs: List[List[int]]) -> int:
        n = len(pairs)
        sorted_pairs = sorted(pairs) 
        dp = [1]*n
        
        for i in range(n):
            for j in range(i+1,n):
                if sorted_pairs[j][0] > sorted_pairs[i][1]:
                    dp[j] = max(dp[j],dp[i]+1)
                    
        return max(dp)
华为在线编程系列 购物单
王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件附件
电脑打印机,扫描仪
书柜图书
书桌台灯,文具
工作椅

如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 ~ 5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j 1 , j 2 ,……, j k ,则所求的总和为:
v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ] 。(其中 * 为乘号)
请你帮助王强设计一个满足要求的购物单。

输入描述:
输入的第 1 行,为两个正整数,用一个空格隔开:N m

(其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q

(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)

输出描述:
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。
示例1
输入
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
输出
2200

solution
0/1背包问题

dp[i][j] : 第i个物品,消耗的钱j
如果当前是主物品:买or不买 dp[i][j] = max(dp[i-1][j],dp[i-1][j-v[i]]+value)
如果当前是附物品:买or不买 dp[i][j] = max(dp[i-1][j],dp[i-1][j-v[i]-v[q[i]-1]+value)
上述两个状态转移方程的前提是v[i]<=j

while True:
    try:
        n,m = list(map(int,input().split()))
        v = [0]*m
        p = [0]*m
        q = [0]*m
        
        for i in range(m):
            v[i],p[i],q[i] = map(int,input().split())
            
        dp = [[0]*(n+1) for i in range(m+1)]
        
        
        for i in range(1,m+1):
            for j in range(1,n+1):
                if q[i-1] == 0:
                    if v[i-1] <= j:
                        dp[i][j] = max(dp[i-1][j],dp[i-1][j-v[i-1]]+v[i-1]*p[i-1])
                    
                if q[i-1] > 0:
                    if v[i-1]+v[q[i-1]-1] <= j:
                        dp[i][j] = max(dp[i-1][j],dp[i-1][j-(v[i-1]+v[q[i-1]-1])+v[i-1]*p[i-1] ])
        
        print(dp[-1][-1])
    except:
        break
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值