leetcode
leetcode 551 ~ 600
K 个逆序对数组

K 个逆序对数组

难度:

标签:

题目描述

代码结果

运行时间: 196 ms, 内存: 0.0 MB


/*
  思路:
  使用动态规划和Java Stream API来计算 dp 数组。与传统动态规划类似,利用前缀和来优化内部循环。
  通过计算前缀和来减少时间复杂度。dp[i][j] = prefix[j] - prefix[j - i]。
  初始化:dp[0][0] = 1,其他 dp[0][j] = 0(j > 0)。
  我们要求的结果是 dp[n][k],结果需要对 10^9 + 7 取模。
*/
import java.util.stream.IntStream;
 
public class KInversePairsStream {
    public int kInversePairs(int n, int k) {
        int MOD = 1000000007;
        int[][] dp = new int[n + 1][k + 1];
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            int[] prefix = new int[k + 1];
            prefix[0] = dp[i - 1][0];
            IntStream.range(1, k + 1).forEach(j -> prefix[j] = (prefix[j - 1] + dp[i - 1][j]) % MOD);
            IntStream.range(0, k + 1).forEach(j -> {
                dp[i][j] = prefix[j];
                if (j >= i) {
                    dp[i][j] = (dp[i][j] - prefix[j - i] + MOD) % MOD;
                }
            });
        }
        return dp[n][k];
    }
}

解释

方法:

这个题解使用动态规划来解决问题。定义状态 dp[i][j] 表示使用从 1 到 i 的整数构成长度为 i 的排列,且恰好有 j 个逆序对的不同排列个数。状态转移方程为:dp[i][j] = dp[i-1][j] + dp[i-1][j-1] + ... + dp[i-1][j-i+1],即在前 i-1 个数的排列中,再插入第 i 个数,可以产生 0 到 i-1 个新的逆序对。通过累加前 i-1 个数排列的逆序对个数来计算 dp[i][j]。最后返回 dp[n][k] 即为答案。

时间复杂度:

O(n^2k)

空间复杂度:

O(nk)

代码细节讲解

🦆
在动态规划的状态转移方程中,为什么是累加`dp[i-1][j]`到`dp[i-1][j-i+1]`的值来计算`dp[i][j]`?
在动态规划中,这种累加是为了计算所有可能的方式,在已有的排列中插入一个新的数字i而形成新的逆序对数目。具体地,考虑插入数字i到一个已有的排列中,这个数字可以插入在排列的任意位置,从最后一位(不增加逆序对)到第一位(增加i-1个逆序对)。因此,要计算恰好有j个逆序对的排列数,我们需要累加所有可能通过插入i到不同位置从而形成j个逆序对的排列数,这些就是`dp[i-1][j]`到`dp[i-1][j-i+1]`。
🦆
如何理解在插入第i个数时,能产生从0到i-1个新的逆序对?
插入第i个数时可以选择插入到现有排列的不同位置,从第一个位置插入到最后一个位置。若插入到最后,则不产生新的逆序对(0个逆序对)。若插入到第一个位置,则所有i-1个已经存在的数都会与这个新插入的数形成逆序对(i-1个逆序对)。因此,插入位置的不同直接影响了新产生的逆序对的数量,范围从0到i-1。
🦆
在代码中,累加操作为什么需要在`j >= i`时减去`dp[i-1][j-i]`?这是如何帮助维护正确的逆序对计数的?
在累加操作中,我们需要维护一个滑动窗口的和,这个和表示插入数字i到不同位置产生的新逆序对的数量。当`j >= i`时,意味着我们需要从总和中去掉那些不可能通过插入i来达到的逆序对数目,即`dp[i-1][j-i]`。这是因为当我们从i-1个元素的排列中取出任何超过j个逆序对的排列时,插入i无法达到恰好j个逆序对的目标。这样的操作确保了我们计数的精确性,维护了一个有效的窗口范围。
🦆
题解中提到的边界条件`dp[1][0] = 1`是基于什么理论或逻辑得出的?
边界条件`dp[1][0] = 1`基于这样一个事实:当只有一个数字1时,只存在一种排列(即[1]),且这个排列中没有任何逆序对。因为只有一个元素,不可能与其他元素形成逆序对,因此逆序对的数量为0。这是初始化动态规划表时的基础情况,确保了从这一基础情况开始,可以正确计算出更大的n和k的情况。

相关问题