Java 中的平方数组数量

2024年9月10日 | 阅读 9 分钟

提供一个仅包含正数的数组作为输入。我们需要找出数组的平方数排列的总数。当相邻两个元素的和是一个完全平方数时,该数组被称为平方数数组。

示例 1

输入

int inArr[] = {1, 3, 6}

输出

2

解释

对于给定的数组,有 6 种可能的排列:{1, 3, 6}, {1, 6, 3}, {3, 1, 6}, {3, 6, 1}, {6, 1, 3}, {6, 3, 1}。在所有这些排列中,只有 {1, 3, 6} 和 {6, 3, 1} 构成了平方数数组。对于 {1, 3, 6},我们有 1 + 3 = 4 (22) 和 3 + 6 = 9 (32);对于 {6, 3, 1},我们有 6 + 3 = 9 (32) 和 1 + 3 = 4 (22)。

示例 2

输入

int inArr[] = {24, 48, 1}

输出

2

解释

对于给定的数组,有 6 种可能的排列:{1, 24, 48}, {1, 48, 24}, {24, 1, 48}, {24, 48, 1}, {48, 1, 24}, {48, 24, 1}。在所有这些排列中,只有 {24, 1, 48} 和 {48, 1, 24} 构成了平方数数组。对于 {24, 1, 48},我们有 24 + 1 = 25 (52) 和 1 + 48 = 49 (72);对于 {48, 1, 24},我们有 48 + 1 = 49 (72) 和 1 + 24 = 25 (52)。

示例 2

输入

int inArr[] = {0, 0, 4, 0, 0}

输出

5

解释

对于给定的数组,有 5 种可能的排列:{0, 0, 0, 0, 4}, {0, 0, 0, 4, 0}, {0, 0, 4, 0, 0}, {0, 4, 0, 0, 0}, {4, 0, 0, 0, 0}。所有这些排列都是平方数数组。对于 {0, 0, 0, 0, 4},我们有 0 + 0 = 0 (02),0 + 0 = 0 (02),0 + 0 = 0 (02),0 + 4 = 4 (22)。其他排列也可以给出类似的逻辑。

简单方法:暴力法

在这种方法中,我们检查输入数组的所有排列。对于输入数组 'inAr' 中的每个排列,检查 inAr[j] + inAr[j+1] 是否为完全平方数。如果是完全平方数,则将 ans 加 1。然后返回 ans。

算法

  • 检查 'inAr' 的特定排列是否为平方数的方法。
    • 创建一个方法 bool checkSquareful (int inAr[], int n)
    • 将 'j' 从 0 迭代到 'inAr' 的长度。
      • 如果 floor(sqrt(inAr[i] + inAr[i+1] )) != ceil(sqrt(inAr[i] + inAr[i+1])), 则返回 false。
    • 否则,返回 false。
  • 计算所有平方数排列的方法
    • 初始化一个 int 'cnt' = 0;
    • 将数组 'inAr' 按升序排序。
    • 运行一个 do-while 循环来检查所有平方数排列。
    • do -> if checkSquareful (inAr) ,则增加 'cnt'。
    • 同时继续检查 'inAr' 的下一个排列。

现在观察以下实现。

文件名: SquarefulArrays.java

输出

For the input array: 
0 0 0 0 4 
The number of Squareful array is: 5

For the input array: 
1 3 6 
The number of Squareful array is: 2

For the input array: 
24 48 1 
The number of Squareful array is: 2

复杂度分析: 对于 N 大小的数组,总共有 N! 种排列。我们检查所有排列,每个排列的大小为 N。因此,程序的总时间复杂度为 O(N! * N)。程序空间复杂度为 O(1),因为程序不使用任何额外的空间。

程序的 time complexity 非常高,不适合较大的输入。因此,需要进行一些优化。观察以下方法。

方法:动态规划

构建一个图,其中如果 inAr[j] + inAr[k] 是完全平方数,则从 j 到 k 存在一条边。然后,计算所有可能的哈密顿路径。哈密顿路径的总数将等于平方数排列的数量。

哈密顿路径是图中两个顶点之间的图路径,该路径至少访问图中的所有顶点一次。

我们将构建一个图,其中在 'j' 和 'k' 之间存在一条边,仅当 inAr[j] + inAr[k] 是完全平方数时。我们可以使用动态规划技术来检查哈密顿路径。

算法

步骤 1:我们将首先消除重复项,只考虑索引排列。P[B, j] 表示已使用的数字数量为 'B' 时,inAr[j] 是最后一个数字的总排列数。

步骤 2: 'B' 是二进制数,其中每一位表示 'j' 是否存在。

步骤 3: 开始时,P[1 << j, j] = 1。其他状态为 0。

步骤 4: 每次,枚举 'k'。

  • 如果 'k' 不等于 'S',并且 inAr[j] + inAr[k] 是完全平方数,那么,
    • P[B|1 << k, k] += P[B, j]

步骤 5: 使用一个变量 'ans'。将其赋值为 0。

步骤 6: 使用 'j' 从 0 迭代到 's',其中输入数组 'inAr' 的大小为 's'。

  • 我们得到,'ans' += P[(1 << s) - 1, j]

步骤 7: 需要对重复的元素计数一次。因此,将 'ans' 除以 'inAr[]' 中每个数字的计数阶乘。

文件名:SquarefulArrays.java

输出

For the input array: 
0 0 0 0 4 
The number of Squareful array is: 5

For the input array: 
1 3 6 
The number of Squareful array is: 2

For the input array: 
24 48 1 
The number of Squareful array is: 2

复杂度分析: 总共有 'N x 2N' 个状态,每个状态最多有 'N' 个子节点。此外,每个子节点至少被遍历一次。因此,程序的总时间复杂度为 O(N2 x 2N)。此外,程序使用二维数组 'P',这使得程序的空间复杂度为 O(N x 2N),其中 N 是输入数组的大小。