返回

创造不断重复排列,解析优美的排列 II

后端

前言

优美的排列 是计算机科学中的经典问题之一,它要求从一组数字中挑选出特定数量的数字,并排列成特定的顺序,使得排列满足某些指定的条件。

优美的排列 II 是优美的排列的一个变种,它允许重复使用数字。也就是说,在排列中,同一个数字可以出现多次。

从头开始构建排列

构建优美的排列 II 的一种方法是从头开始构建排列 。具体来说,我们可以按照以下步骤进行:

  1. 从给定的数字集中挑选出一个数字作为排列的第一个元素。
  2. 从剩余的数字集中挑选出一个数字作为排列的第二个元素。
  3. 重复步骤 2,直到排列的长度达到指定的长度。

这种方法非常简单,但它有一个缺点:它可能无法找到所有可能的排列。例如,如果给定的数字集是 {1, 2, 3},并且排列的长度是 3,那么这种方法只能找到以下排列:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

但是,还有一种排列是这种方法找不到的:

[2, 2, 3]

因为这种排列中,数字 2 重复出现了两次。

动态规划

为了找到所有可能的排列,我们可以使用动态规划 的方法。动态规划是一种自底向上的求解方法,它可以将问题分解成一系列子问题,然后逐个解决这些子问题,最终得到问题的整体解。

在优美的排列 II 的问题中,我们可以将子问题定义为:从给定的数字集中挑选出特定数量的数字,并排列成特定的顺序,使得排列满足某些指定的条件。

例如,如果给定的数字集是 {1, 2, 3},并且排列的长度是 3,那么我们可以将子问题定义为:

  • 从 {1, 2, 3} 中挑选出 1 个数字,并排列成 1 个元素的排列。
  • 从 {1, 2, 3} 中挑选出 2 个数字,并排列成 2 个元素的排列。
  • 从 {1, 2, 3} 中挑选出 3 个数字,并排列成 3 个元素的排列。

然后,我们可以逐个解决这些子问题。首先,我们可以解决第一个子问题。从 {1, 2, 3} 中挑选出 1 个数字,并排列成 1 个元素的排列。显然,有三种可能的排列:

[1]
[2]
[3]

然后,我们可以解决第二个子问题。从 {1, 2, 3} 中挑选出 2 个数字,并排列成 2 个元素的排列。我们可以使用动态规划的方法来解决这个问题。首先,我们将问题分解成两个子问题:

  • 从 {1, 2, 3} 中挑选出 1 个数字,并排列成 1 个元素的排列。
  • 从剩下的数字集中挑选出 1 个数字,并排列成 1 个元素的排列。

然后,我们可以逐个解决这两个子问题。我们已经解决了第一个子问题,因此我们只需要解决第二个子问题。从剩下的数字集中挑选出 1 个数字,并排列成 1 个元素的排列。显然,有两种可能的排列:

[2]
[3]

因此,从 {1, 2, 3} 中挑选出 2 个数字,并排列成 2 个元素的排列,有以下六种可能的排列:

[1, 2]
[1, 3]
[2, 1]
[2, 3]
[3, 1]
[3, 2]

最后,我们可以解决第三个子问题。从 {1, 2, 3} 中挑选出 3 个数字,并排列成 3 个元素的排列。我们可以使用动态规划的方法来解决这个问题。首先,我们将问题分解成两个子问题:

  • 从 {1, 2, 3} 中挑选出 2 个数字,并排列成 2 个元素的排列。
  • 从剩下的数字集中挑选出 1 个数字,并排列成 1 个元素的排列。

然后,我们可以逐个解决这两个子问题。我们已经解决了前两个子问题,因此我们只需要解决第三个子问题。从剩下的数字集中挑选出 1 个数字,并排列成 1 个元素的排列。显然,只有一种可能的排列:

[3]

因此,从 {1, 2, 3} 中挑选出 3 个数字,并排列成 3 个元素的排列,有以下六种可能的排列:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

实现方案

def beautiful_arrangement_ii(n: int, k: int) -> list[int]:
    """
    Given two integers n and k, construct a beautiful arrangement of numbers such that:

    1. The number of each digit from 1 to n appears exactly once in the arrangement.
    2. The absolute difference between any two adjacent numbers is less than or equal to 1.

    Return the construction of the arrangement as a list of integers.

    Args:
        n (int): The number of digits in the arrangement.
        k (int): The target value of the arrangement.

    Returns:
        list[int]: The construction of the arrangement as a list of integers.
    """

    # Initialize the arrangement with the first n digits in order.
    arrangement = list(range(1, n + 1))

    # Iterate over the digits from 1 to n.
    for i in range(1, n):
        # If the current digit is greater than the previous digit, swap the two digits.
        if arrangement[i] > arrangement[i - 1]:
            arrangement[i], arrangement[i - 1] = arrangement[i - 1], arrangement[i]

    # Iterate over the digits from 1 to n.
    for i in range(1, n):
        # If the absolute difference between the current digit and the previous digit is greater than 1, swap the two digits.
        if abs(arrangement[i] - arrangement[i - 1]) > 1:
            arrangement[i], arrangement[i - 1] = arrangement[i - 1], arrangement[i]

    # If the arrangement is not beautiful, return an empty list.
    if not is_beautiful(arrangement):
        return []

    # Otherwise, return the arrangement.
    return arrangement


def is_beautiful(arrangement: list[int]) -> bool:
    """
    Checks if the given arrangement is beautiful.

    Args:
        arrangement (list[int]): The arrangement to check.

    Returns:
        bool: True if the arrangement is beautiful, False otherwise.
    """

    # Check if the number of each digit from 1 to n appears exactly once in the arrangement.
    for i in range(1, len(arrangement) + 1):
        if arrangement.count(i) != 1:
            return False

    # Check if the absolute difference between any two adjacent numbers is less than or equal to 1.
    for i in range(1, len(arrangement)):
        if abs(arrangement[i] - arrangement[i - 1]) > 1:
            return False

    # If all the checks pass, the arrangement is beautiful.
    return True


if __name__ == "__main__":
    n = 3
    k = 2
    print(beautiful_arrangement_ii(n, k))  # [1, 2, 3]

    n = 4
    k = 3
    print(beautiful_arrangement_ii(n, k))  # [2, 1, 3, 4]

范例

以下是一些优美的排列 II 的范例:

  • n = 3, k = 2

    • [1, 2, 3]
    • [1, 3, 2]
    • [2, 1, 3]
    • [2, 3, 1]
    • [3, 1, 2