分区数组的方式数

时间:2017-06-23 19:03:42

标签: arrays algorithm

  

给定一个n元素数组,数组的k分区是将数组拆分为k个连续的子数组,以使子数组的最大值不增加。即max(subarray1) >= max(subarray2) >= ... >= max(subarrayK)

     

如何多种方式可以将数组划分为有效的分区,如前面提到的那样?

     

注意k未作为输入或任何内容提供,我mereley用它来说明一般情况。分区的大小可以是1n,我们只需找到所有有效的分区。

例如,数组[3,2,1]可以用4种方式分区,你可以在下面看到它们:

有效分区:[3,2,1]; [3,[2,1]]; [[3,2],1]; [[3],[2],[1]]。

我发现了与线性分区相关的类似问题,但我无法找到一种方法来适应这个问题。我很确定这是动态编程,但我还没有能够正确识别 如何使用递归关系对问题建模。 你会如何解决这个问题?

2 个答案:

答案 0 :(得分:0)

如果输入元素至少与后面的所有元素一样大,则调用 tail-max 的输入元素。例如,在以下输入中:

5 9 3 3 1 2

以下元素是尾部最大值:

5 9 3 3 1 2
  ^ ^ ^   ^

在有效分区中,每个子数组必须包含子数组起始位置或之后的下一个尾部最大值;否则,下一个尾部最大值将是某个后续子阵列的最大值,并且将违反非增加子阵列最大值的条件。

另一方面,如果每个子数组在子数组的起始位置或之后包含下一个尾部最大值,则该分区必须有效,因为尾部最大值的定义确保后续子数组的最大值不能是大。

如果我们识别数组的尾部最大值,例如

1 1 9 2 1 6 5 1
. . X . . X X X

其中X表示尾部最大值和。意思是没有,那么我们就不能在第一个尾部最大值之前放置任何子阵列边界,因为如果我们这样做,第一个子阵列将不包含尾部最大值。我们最多可以在一个尾部最大值和下一个之间放置一个子阵列边界;如果我们放置更多,我们得到一个不包含尾部最大值的子阵列。最后一个tail-max必须是输入的最后一个元素,因此我们不能在最后一个tail-max之后放置一个子数组边界。

如果尾部最大值与下一个之间存在m个非尾部最大元素,则会为我们提供m+2个选项:m+1放置数组边界的位置,或者我们可以选择不在这些元素之间放置边界。这些因素是乘法的。

我们可以从输入的末尾到开头进行一次传递,确定尾部最大值之间的间隙长度,并将适当的因子相乘以在O(n)时间内解决问题:

def partitions(array):
    tailmax = None
    factor = 1
    result = 1
    for i in reversed(array):
        if tailmax is None:
            tailmax = i
            continue
        factor += 1
        if i >= tailmax:
            # i is a new tail-max.
            # Multiply the result by a factor indicating how many options we
            # have for placing a boundary between i and the old tail-max.
            tailmax = i
            result *= factor
            factor = 1
    return result

答案 1 :(得分:-1)

更新:抱歉,我误解了这个问题。在这种情况下,将数组拆分为子数组,其中每个尾部是数组中的最大元素,然后它将在狭窄的情况下工作。例如[2 4 5 9 6 8 3 1]将首先分为[[2 4 5 9] 6 8 9 3 1]。然后我们可以自由选择范围0 - 5来决定是否包括以下内容。您可以使用数组记录DP的结果。我们的目标是res[0]。我们在上面的示例中已经有res[0] = res[5] + res[6] + res[7] + res[8] + res[9] + res[10],res [10] = 1

def getnum(array):
    res = [-1 for x in range(len(array))]
    res[0] = valueAt(array, res, 0)
    return res[0]

def valueAt(array, res, i):
    m = array[i]
    idx = i
    for index in range(i, len(array), 1):
        if array[index] > m:
            idx = index
            m = array[index]
    value = 1;
    for index in range(idx + 1, len(array), 1):
        if res[index] == -1:
            res[index] = valueAt(array, res, index)
        value = value + res[index]
    return value;

比上面的答案更耗费时间。 DP总是花费很多。

旧答案:如果允许数组中没有重复的元素,则以下方式可行:

请注意,如果没有重复,子数组的数量不取决于元素的值。如果数组中有n个元素,我们可以注意数字为N(n)

最大元素必须位于第一个子数组中,其他元素可以存在于第一个子数组中或不存在于第一个子数组中。取决于它们是否在第一个子数组中,其余元素的分区数会有所不同。

所以, N(n) = C(n-1, 1)N(n-1) + C(n-1, 2)N(n-2) + ... + C(n-1, n-1)N(0)

其中C(n,k)表示:

enter image description here

然后它可以由DP解决。

希望这有帮助