人类高耸的算法

时间:2013-07-22 22:44:42

标签: algorithm sorting dynamic-programming

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)


以下是本书中的解决方案

  

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

     

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

     

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

     

b)如果对于下一个项目,身高和体重不大于前一个项目,我们将此项目标记为“不合格”

     

c)如果找到的序列中的项目多于“最大序列”,则变为“最大序列”

     

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


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

Q1

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

例如

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

显然,(6,7)是一个不合适的项目,但(7,8)怎么样?根据解决方案,它并不适合,因为它的h和w比(6,7)更大,但是,它不能被考虑到序列中,因为(7,8)不适合(5,9)。< / p>

我是对的吗?

如果我是对的,修复是什么?

Q2

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

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

5 个答案:

答案 0 :(得分:1)

您可以使用动态编程解决问题。

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

我们计算一个序列T_i,其中T_i是一个塔,其中人i位于最大大小的顶部。 T_1只是{1}。我们可以从早期的T_j中推断出后续的T_k - 找到可以取k个权重的最大塔T_j(w_j&lt; w_k)并在其上站立。

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

此算法需要O(n ** 2)时间,其中n是剧团的基数。

答案 1 :(得分:0)

我自己试图解决这个问题,并不是要给出“现成的解决方案”,但仍然给予,更多的是检查我自己的理解,以及我的代码(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)])   

答案 2 :(得分:0)

  

例如

     

(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。

答案 3 :(得分:0)

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

beginning_index是我们可以考虑从中获取元素的索引(我们可以考虑在人类堆栈上可以考虑下方的人的索引),beginning_tuple指的是元素/更高层的人。

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

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)

答案 4 :(得分:0)

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

算法

  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 方法生成的序列可能不是实际的最长子序列,但是,它的长度 - 肯定是最长递增子序列的长度。
请参阅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])