数据结构与算法分类练习--动态规划

动态规划算法通常基于一个递推公式[状态转移公式]及一个或多个初始状态[边界]。 当前子问题的解将由上一次子问题的解[最优子结构]推出。

使用条件:当问题具有“最优子结构”、“子问题重叠”、“边界”时,就可以用动态规划求解。

动态规划和贪心算法的区别:

贪心不能保证求得的最后解是最佳的;不能用来求最大或最小解问题;  

下面以找钱为例对两个算法进行对比:

如何用1,5,11三种面额的纸币找回15,且使用纸币张数最少。

1. 根据动态规划法的解题思想,得到最优解为5+5+5                                   总共 3张

2. 根据贪心算法的解题思想,得到的近似最优解为11+1+1+1+1                    总共 5张 

Triangle 三角形

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.For example, given the following triangle

[        [2],

       [3,4],

      [6,5,7],

     [4,1,8,3] ]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

为了减小空间复杂度,我们可以复制三角形最后一行res,用来更新记录累加值。从倒数第二行开始,每个数字和它下一行相邻的较小的数字相加,来更新当前的坐标值。比如6是倒数第二行的第一位,选择下一行的相邻数字4,1较小的1进行相加得7,更新res的第一位为7。同理res的第二位更新为6,第三位更新为10。然后再一层一层往上遍历,最后res中的第一个元素就是所求值。

def minimumTotal1(self, triangle):
    """
    :type triangle: List[List[int]]
    :rtype: int
    """
    if not triangle:
        return
    res = triangle[-1]
    for i in xrange(len(triangle)-2, -1, -1):
        for j in xrange(len(triangle[i])):
            res[j] = min(res[j], res[j+1]) + triangle[i][j]
    return res[0]

Maximum Subarray 最大子数组

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [−2,1,−3,4,−1,2,1,−5,4],

the contiguous subarray [4,−1,2,1] has the largest sum = 6.

用两个变量res,s分别保存全局最大值和当前最大值。当前最大值从第一个数值开始往后累加,如果累加值大于res则更新res,如果小于0则置0重新累加。

def maxSubArray(self, nums):
    res, s = None, 0
    for num in nums:
        s += num
        if res is None or s > res:
            res = s
        if s < 0:
            s = 0
    return res

def maxSubArray1(self, nums):
    if max(nums) < 0:
        return max(nums)
    g_max = c_max = nums[0]
    for i in range(1, len(nums)):
        c_max = max(nums[i], c_max + nums[i])
        g_max = max(g_max, c_max)
    return g_max

House Robber 劫富济贫

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

这道题的数学模型就是在一个数组中抽出一些不相邻的数字使其和最大。我们用两个变量last,now分别保存到上一家和到当前这一家最多可以获取到的财富。last的更新公式:last=now;now的更新公式:now=max(last+i, now),即到上上家可以获取到最大财富+当前这一家的财富和到上一家可以获得的最大财富中的较大值。

def rob(self, nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    last, now = 0, 0
    for i in nums: 
        last, now = now, max(last + i, now)
    return now

House Robber II 劫富济贫II

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

在这道题中,房子不再是一字排列而是一个环形,这样的话在第一家和最后一家这两家中最多只能抢一家。如果我们去掉第一家或者最后一家分别求解,取较大值,这样就和上一题是一样的了。

def rob(self, nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    if len(nums) == 0:
        return 0
    if len(nums) == 1:
        return nums[0]
        
    def linerob(nums):
        last, curr = 0, 0
        for i in nums:
            last, curr = curr, max(last + i, curr)
        return curr
        
    return max(linerob(nums[0:len(nums) - 1]), linerob(nums[1:len(nums)]))

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. You may assume that you have an infinite number of each kind of coin.

Example: coins = [1, 2, 5], amount = 11. return 3. (11 = 5 + 5 + 1)

维护一个动态数组amounts[], amounts[n]保存着amount为n时需要的最少硬币数量。从0值开始计算组成当前amount的最少硬币数,amount按照coins中的数值从0开始累加计算,例如1,2,5,2,3,6,3,4,7... 最后查找amounts[amount]是否存在即可。

def coinChange1(self, coins, amount):
    INF = 0x7fffffff  # Using float("inf") would be slower.
    amounts = [INF] * (amount + 1)
    amounts[0] = 0
    for i in xrange(amount + 1):
        if amounts[i] != INF:
            for coin in coins:
                if i + coin <= amount:
                    amounts[i + coin] = min(amounts[i + coin], amounts[i] + 1)
    return amounts[amount] if amounts[amount] != INF else -1

Edit Distance 编辑距离

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)

You have the following 3 operations permitted on a word:

a) Insert a character

b) Delete a character

c) Replace a character

这次需要维护一个二维的数组dp,其中dp[i][j]表示从word1的前i个字符转换到word2的前j个字符所需要的步骤。递推公式可以通过一两个小例子来发现。比如从“bbc”到“abcd“可以得到dp数组值如下:

dp[i][j] =      /    dp[i - 1][j - 1]                                                                   word1[i - 1] == word2[j - 1]

                     \    min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1            word1[i - 1] != word2[j - 1]

    def minDistance1(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        l1, l2 = len(word1)+1, len(word2)+1
        dp = [[0 for _ in xrange(l2)] for _ in xrange(l1)]
        for i in xrange(l1):
            dp[i][0] = i
        for j in xrange(l2):
            dp[0][j] = j
        for i in xrange(1, l1):
            for j in xrange(1, l2):
                dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+(word1[i-1]!=word2[j-1]))
        return dp[-1][-1]

Dungeon Game 地牢游戏

The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dungeon. The dungeon consists of M x N rooms laid out in a 2D grid. Our valiant knight (K) was initially positioned in the top-left room and must fight his way through the dungeon to rescue the princess.

The knight has an initial health point represented by a positive integer. If at any point his health point drops to 0 or below, he dies immediately.

Some of the rooms are guarded by demons, so the knight loses health (negative integers) upon entering these rooms; other rooms are either empty (0's) or contain magic orbs that increase the knight's health (positive integers).

In order to reach the princess as quickly as possible, the knight decides to move only rightward or downward in each step.

Write a function to determine the knight's minimum initial health so that he is able to rescue the princess.

For example, given the dungeon below, the initial health of the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN.

-2 (K) -3 3
-5 -10 1
10 30 -5 (P)

同样需要委会一个二维数组hp,其中hp[i][j]用来表示从当前位置 (i, j) 出发的初始血量,从公主所在的房间开始计算生命值,不断的得到各个位置的最优的生命值直到第一个房间。初始值:公主房到下面和右面分别初始化为1,然后往左上迭代。迭代方程:取右下房间中的较小值-耗血量。如果得到的值小于0说明当前房间里事魔法球可以增加血量,此房间初始化为1即可。

    def calculateMinimumHP(self, dungeon):
        """
        :type dungeon: List[List[int]]
        :rtype: int
        """
        M = len(dungeon)
        N = len(dungeon[0])
        hp = [[sys.maxsize]*(N+1) for i in xrange(M+1)]
        hp[M][N - 1] = 1
        hp[M - 1][N] = 1
        for i in xrange(M-1, -1, -1):
            for j in xrange(N-1, -1, -1):
                need = min(hp[i+1][j], hp[i][j+1]) - dungeon[i][j]
                if need <= 0:
                    hp[i][j] = 1
                else:
                    hp[i][j] = need
        return hp[0][0]

猜你喜欢

转载自blog.csdn.net/ZJL0105/article/details/81436919