2021.11.11LeetCode每日一题——K个逆序对数组

目录

K个逆序对数组

描述

示例 1

示例 2

说明

方法:动态规划


K个逆序对数组

描述

给出两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数。

逆序对的定义如下:对于数组的第 i 个和第 j 个元素,如果满 i < j 且 a[i] > a[j],则其为一个逆序对;否则不是。

由于答案可能很大,只需要返回 答案 mod 10^9 + 7 的值。

示例 1

输入: n = 3, k = 0
输出: 1
解释: 
只有数组 [1,2,3] 包含了从1到3的整数并且正好拥有 0 个逆序对。

示例 2

输入: n = 3, k = 1
输出: 2
解释: 
数组 [1,3,2] 和 [2,1,3] 都有 1 个逆序对。

说明

 n 的范围是 [1, 1000] 并且 k 的范围是 [0, 1000]。

方法:动态规划

假设 f[i][j] 表示使用 [1,i] 的元素构成长度为i,恰好包含j个逆序对数组的个数。

假设我们第 i 个元素(即数组的最后一位)选取了数字 k,那么数组的前i-1个位置由 1,2,...,k-1 以及 k+1,k+2,...,i 的某个排列组成。数组中逆序对的个数可以看成以下两个部分之和:

  • 数字k与前 i-1 个元素产生的逆序对个数
  • 前 i-1 个元素内部产生的逆序对个数

对于第一部分,我们可以求出数字k会贡献 i-k 个逆序对(即和元素k+1,k+2,...,i产生逆序对)。

对于第二部分,我们希望他能有 j-(i-k) 个逆序对,这样才能一共有j个逆序对。

由于我们关心的是前 i-1 个元素内部产生的逆序对个数,而逆序对个数只跟元素的相对大小有关,所以我们可以:

  • 1,2,...,k-1这些元素保持不变
  • k+1,k+2,...,i这些元素-1,变为k,k+1,...,i-1

所以我们的目标变为了求使用 [1,i-1] 的元素构成长度为i-1,恰好包含 j-(i-k) 个逆序对数组的个数。也就对应着我们定义的f[i-1][j-(i-k)]。

所以我们可以得到状态转移方程:

f[i][j]=\sum_{k=1}^i f[i-1][j-(i-k)]=\sum _{k=0}^{i-1}f[i-1][j-k]

其中f[0][0]=1,最终返回f[n][k]的值即可。

上述动态规划的状态数量为 O(nk),而求出每一个 f[i][j] 需要 O(n) 的时间复杂度,因此总时间复杂度为 O(n^2k),会超出时间限制,我们必须要进行优化。

我们考虑 f[i][j-1] 和 f[i][j] 的状态转移方程:

\left\{\begin{matrix} f[i][j-1]=\sum _{k=0}^{i-1}f[i-1][j-1-k] \\ f[i][j]=\sum _{k=0}^{i-1}f[i-1][j-k] \end{matrix}\right.

可以得到从 f[i][j-1] 到 f[i][j] 的递推式:

f[i][j]=f[i][j-1]-f[i-1][j-i]+f[i-1][j]

这样我们就可以在 O(1) 的时间计算出每个 f[i][j],总时间复杂度降低为 O(nk)。

此外,由于 f[i][j] 只会从第 f[i-1][..] 和 f[i][..] 转移而来,因此我们可以对动态规划使用的空间进行优化,即使用两个一维数组交替地进行状态转移,空间复杂度从 O(nk) 降低为 O(k)。

class Solution {
    public int kInversePairs(int n, int k) {
        final int MOD = 1000000007;
        int[][] f = new int[2][k + 1];
        f[0][0] = 1;
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j <= k; ++j) {
                int cur = i & 1, prev = cur ^ 1;
                f[cur][j] = (j - 1 >= 0 ? f[cur][j - 1] : 0) - (j - i >= 0 ? f[prev][j - i] : 0) + f[prev][j];
                if (f[cur][j] >= MOD) {
                    f[cur][j] -= MOD;
                } else if (f[cur][j] < 0) {
                    f[cur][j] += MOD;
                }
            }
        }
        return f[n & 1][k];
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_39478524/article/details/121260900