📌  相关文章
📜  元素最多为 K 的不同 N 大小数组的计数,以便相邻元素对是升序或非倍数(1)

📅  最后修改于: 2023-12-03 15:22:31.726000             🧑  作者: Mango

元素最多为 K 的不同 N 大小数组的计数,以便相邻元素对是升序或非倍数

这个问题要求我们计算给定大小为 N 的、元素最多为 K 的数组中,有多少种排列方式,满足相邻元素对是升序或非倍数。

解法概述

这个问题的解法可以用动态规划来求解,具体来说,我们可以建立一个二维数组 $dp$,其中 $dp[i][j]$ 表示前 $i$ 个数中,以第 $i$ 个数结尾,且最大元素为 $j$ 的排列数量。

考虑如何转移状态,对于第 $i$ 个数,它可以插入到以前 $i-1$ 个数中的任何一个位置,因此有以下两种情况:

  1. 假设我们选择把第 $i$ 个数插入到当前最长长度为 $l$($l \leq i-1$)的 LIS(最长递增子序列)中,此处我们令当前 LIS 的结尾元素为 $last$。那么要满足相邻元素对是升序,并且最大元素为 $j$,那么说明第 $i$ 个数必须落在区间 $[last+1,j)$ 中,因为当前最大元素为 $j$,并且我们要保证相邻元素对是升序,那么第 $i$ 个数必须大于 $last$(否则相邻元素对不升序),并且小于等于 $j$ (否则最大元素不为 $j$)。因此,我们需要枚举一个 $last$,并在 $[last+1,j)$ 中选择一个数插入,在此基础上,把 $dp[i-1][last]$ 加到 $dp[i][j]$ 中。

  2. 如果我们选择把第 $i$ 个数插入到前 $i-1$ 个数的末尾,即不将其插入到当前最长的 LIS 中。那么要满足相邻元素对是非倍数,并且最大元素为 $j$,那么说明第 $i$ 个数必须落在区间 $[1,j) \setminus {2k}$ 中,其中 $k$ 是任何前 $i-1$ 个数组成元素。因此,我们需要枚举前 $i-1$ 个数的结尾元素 $last$,并在 $[1,j) \setminus {2k}$ 中选择一个数插入,在此基础上,把 $dp[i-1][last]$ 加到 $dp[i][j]$ 中。

最终的答案就是 $\sum_{j=1}^{K} dp[N][j]$,表示最后一个数为第 $i$ 个数,最大元素为 $j$ 的所有方案数之和。

代码实现
def count_arrays(n: int, k: int) -> int:
    MAXN = n + 1
    dp = [[0] * (k + 1) for _ in range(MAXN)]
    dp[0][0] = 1
    for i in range(1, n+1):
        # case 1: insert i-th number into LIS
        for last in range(i):
            for j in range(last+1, k+1):
                dp[i][j] += dp[i-1][last]
        # case 2: insert i-th number into end of array
        for last in range(i):
            for j in range(1, k+1):
                if j * 2 not in range(1, k+1):
                    dp[i][j] += dp[i-1][last]
    ans = sum(dp[n])
    return ans
时间复杂度

动态规划的时间复杂度是 $O(NK^2)$,在本问题下,$N$、$K$ 都比较小,因此可以接受。