【问题标题】:Algorithm for Human Towering人类高耸的算法
【发布时间】:2013-07-21 20:18:00
【问题描述】:

Cracking the Coding Interview, Fourth Edition,有这样一个问题:

马戏团正在设计一个由站着的人组成的塔式程序 在另一个人的肩膀上 出于实用和美观的原因, 每个人都必须比他下面的人更矮更轻 或她 鉴于马戏团中每个人的身高和体重, 编写一个计算最大可能人数的方法 这样的塔。

示例:输入 (ht, wt): (65, 100) (70, 150) (56, 90) (75, 190) (60, 95) (68, 110)

输出:最长的塔长为 6 并且 从上到下包括: (56, 90) (60,95) (65,100) (68,110) (70,150) (75,190)


这是它的书中的解决方案

Step 1 首先按高度排序所有物品,然后按重量排序这意味着如果所有高度都是唯一的,那么物品将按高度排序如果高度相同,则物品将按重量排序

步骤 2 找到包含增加高度和增加重量的最长序列 为此,我们:

a) 从序列的开头开始 目前,max_sequence 为空

b) 如果下一件物品的身高和体重不大于上一件物品,我们将该物品标记为“不合适”

c) 如果找到的序列的项数超过“最大序列”,则变为“最大序列”

d) 之后从“不合适的项目”开始重复搜索,直到我们到达原始序列的末尾


我对其解决方案有一些疑问。

第一季度

我认为这个解决方案是错误的。

例如

(3,2) (5,9) (6,7) (7,8)

很明显,(6,7) 是一个不合适的项目,但是(7,8) 呢?根据解决方案,它不是不合适的,因为它的 h 和 w 都比(6,7) 大,但是,它不能被考虑到序列中,因为(7,8) 不适合(5,9)

我说的对吗?

如果我是对的,解决方法是什么?

第二季度

我相信即使上面的解决方案有修复,解决方案的风格至少会导致O(n^2),因为它需要一次又一次地迭代,根据步骤2-d。

那么有可能有一个 O(nlogn) 的解决方案吗?

【问题讨论】:

  • 澄清。步骤 b 说“......标记这个项目......”但是“这个”项目是步骤第一部分的“上一个”还是“下一个”项目?其次,如果你发现一个“不合适”的项目,它会被跳过吗?它会结束序列吗?我也不明白步骤 D 中的“搜索重复”是什么意思。
  • 该算法可能正确,也可能不正确,但它的描述方式似乎使它不受大多数​​理解它的尝试的影响......如果这是书中的逐字记录,我会摆脱它.
  • 与 JacobRaihle 所说的相呼应,那本书似乎没有经过很好的校对。我记得阅读前几章并在发现的解决方案/边缘案例中发现了一些错误。它很好地概述了你应该在准备中涵盖的主题,但它所涵盖的任何信息似乎都不是特别权威。
  • 每当我需要在 C# 中实现 IComparable 方法 ToCompare 时遇到问题,应该返回一个 int,以检查两个对象是否比彼此更高、更小或更大,以定义比较标准, 因为你可以有 5 个选项:(H1 > H2 and W1 H2 and W1 > W2), (H1 W2), (H1 == H2 和 W1 == W2)

标签: algorithm sorting dynamic-programming


【解决方案1】:

你可以用动态规划来解决这个问题。

按高度对剧团进行排序。为简单起见,假设所有高度 h_i 和权重 w_j 都是不同的。因此 h_i 是一个递增序列。

我们计算一个序列 T_i,其中 T_i 是一座塔,其中人 i 位于最大尺寸的顶部。 T_1 就是 {1}。我们可以从前面的 T_j 推导出后续的 T_k——找到最大的可以承受 k 重量的塔 T_j (w_j

剧团中最大的塔是 T_i 中最大的。

该算法耗时 O(n**2),其中 n 是剧团的基数。

【讨论】:

  • 按 O(n**2),你的意思是 O(n^2)?
  • @JacksonTale 是的,蛇佬腔,这是 Python 语法。
  • @ColonelPanic:如果身高和体重不是唯一的怎么办?
【解决方案2】:

我自己尝试解决这个问题,并不是要给出“现成的解决方案”,但仍然给出更多内容来检查我自己的理解以及我的代码(Python)是否正常并且适用于所有测试用例。我尝试了 3 个案例,它似乎是正确答案的工作。

#!/usr/bin/python
#This function takes a list of tuples. Tuple(n):(height,weight) of nth person
def htower_len(ht_wt):
    ht_sorted = sorted(ht_wt,reverse=True)
    wt_sorted = sorted(ht_wt,key=lambda ht_wt:ht_wt[1])
    max_len = 1 

    len1 = len(ht_sorted)
    i=0
    j=0
    while i < (len1-1):
        if(ht_sorted[i+1][1] < ht_sorted[0][1]):
            max_len = max_len+1
        i=i+1           

    print "maximum tower length :" ,max_len

###Called above function with below sample app code.
testcase =1 
print "Result of Test case ",testcase   
htower_len([(5,75),(6.7,83),(4,78),(5.2,90)])

testcase = testcase + 1
print "Result of Test case ",testcase   
htower_len([(65, 100),(70, 150),(56, 90),(75, 190),(60, 95),(68, 110)])

testcase = testcase + 1
print "Result of Test case ",testcase   

htower_len([(3,2),(5,9),(6,7),(7,8)])   

【讨论】:

  • 这也是动态规划吗?
  • 不,这不是动态编程方法。
【解决方案3】:

例如

(3,2) (5,9) (6,7) (7,8)

显然,(6,7) 是不合适的项目,但 (7,8) 呢?

回答您的问题 - 算法首先从 3,2 开始运行,并获取序列 (3,2) (5,9) 将 (6,7) 和 (7,8) 标记为不合适。

然后它再次从 (6,7) 开始(第一个不合适的)并得到 (6,7) (7,8),这使得答案为 2。由于没有更多“不合适”的项目,序列以最大长度 2 终止。

【讨论】:

  • 但是如果你放弃了 (5,9) 的考虑,你可能会得到 3 的运行结果。所以这不是一个很好的算法,或者它没有得到很好的解释。
  • 是的,这有点奇怪,当它显然不能按规定工作时,它进入了一本书。
  • 我的意思是,我可以想出几种方法来制作一个有效的算法,但我不确定它们中的任何一种是否代表了作者试图达到的精神。
【解决方案4】:

首先按高度和重量对数组进行排序后,我的代码会检查如果我们抓取数组中剩余的任何元组(以及可能的后续元组),最大的塔会是什么。为了避免重新计算子问题,solution_a用于存储从input_array的尾部开始的最佳最大长度。

beginning_index 是我们可以考虑从中抓取元素的索引(我们可以从中考虑可以在人类堆栈中低于的人的索引),beginning_tuple 是指更高的元素/人堆栈。

此解决方案在 O(nlogn) 中运行以进行排序。 solution_a 数组和 input_array 的副本使用的空间为 O(n)。

def determine_largest_tower(beginning_index, a, beginning_tuple, solution_a):
    # base case
    if beginning_index >= len(a):
        return 0
    if solution_a[beginning_index] != -1:   # already computed
        return solution_a[beginning_index]

    # recursive case
    max_len = 0
    for i in range(beginning_index, len(a)):
        # if we can grab that value, check what the max would be
        if a[i][0] >= beginning_tuple[0] and a[i][1] >= beginning_tuple[1]:
            max_len = max(1 + determine_largest_tower(i+1, a, a[i], solution_a), max_len)
    solution_a[beginning_index] = max_len
    return max_len

def algorithm_for_human_towering(input_array):
    a = sorted(input_array)
    return determine_largest_tower(0, a, (-1,-1), [-1] * len(a))

a = [(3,2),(5,9),(6,7),(7,8)]
print algorithm_for_human_towering(a)

【讨论】:

    【解决方案5】:

    这是用代码完全解决问题的另一种方法;

    算法

    1. 先按高度再按宽度排序

    排序数组: [(56, 90), (60, 95), (65, 100), (68, 110), (70, 150), (75, 190)]

    1. 找出权重的最长递增子序列的长度

    为什么最长的权重子序列是答案? 人是按身高排序的, 所以当我们找到体重增加的人的子序列时 这些被选中的人将满足我们的要求,因为他们的身高和体重都是递增的,因此可以形成一个人塔。

    例如: [(56, 90) (60,95) (65,100) (68,110) (70,150) (75,190)]

    高效实施

    在附加的实现中,我们维护了一个递增数字的列表,并使用bisect_left(在底层使用二分搜索实现)来找到插入的正确索引。

    请注意; longest_increasing_sequence 方法生成的序列可能不是实际最长的子序列,但是它的 length - 肯定是最长递增子序列的长度。
    详情请咨询Longest increasing subsequence Efficient algorithms

    根据需要,总体时间复杂度为 O(n log(n))。

    代码

    from bisect import bisect_left
    
    def human_tower(height, weight):
        def longest_increasing_sequence(A, get_property):
            lis = []
            for i in range(len(A)):
                x = get_property(A[i])
                i = bisect_left(lis, x)
                if i == len(lis):
                    lis.append(x)
                else:
                    lis[i] = x
            return len(lis)
        # Edge case, no people
        if 0 == len(height):
            return 0
        # Creating array of heights and widths
        people = [(h, w) for h, w in zip(height, weight)]
        # Sorting array first by height and then by width
        people.sort()
        # Returning length longest increasing sequence
        return longest_increasing_sequence(people, lambda t : t[1])
    
    assert 6 == human_tower([65,70,56,75,60,68], [100,150,90,190,95,110])
    

    【讨论】:

      猜你喜欢
      • 2015-11-20
      • 1970-01-01
      • 1970-01-01
      • 2018-09-03
      • 2011-02-02
      • 2017-11-05
      • 1970-01-01
      • 2019-10-14
      • 1970-01-01
      相关资源
      最近更新 更多