题目描述
困难题
给出两个整数 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]。
思路
动态规划,用n+1 * k+1 的dp数组,dp[i][j]表示长度为i逆序对为j的数组个数
初始化,dp[i][0] = 1 ,长度任意,逆序对为0,也就是顺序排
其余为0
状态转移,考虑一个稍微长一点的例子 n = 5,k = 1;
在加上5之前,4的排列有很多种,不一一列举,举例考虑1 2 4 3,1 2 3 4
当需要1个逆序对时,一种是原来就有一个逆序对,那5就加到最后,一种,即dp[4][1];
或者原来没有逆序对,我用5添加一个逆序对,5有 _1_2_3_4_五个位置可以插入
插到3_4之间时会构造一个逆序对,多往前一位便多一个逆序对,所以也是一种,即dp[4][0];
所以dp[i][j]应该等于所有符合条件的dp[i-1][x]的累加,
而条件就是 原有逆序对 + 插入新增逆序对 能等于 j,
而新增最多能有i-1对,(1_2_3_4,5插到头,新增4个逆序对)
所以,不超过边界的话,dp[i][j] = dp[i-1][j-(i-1)] 加到 dp[i-1][j];
当超过边界时候,那就是从dp[i-1][0] 加到 dp[i-1][j]
这里强烈建议自己举例子写写
这样每次累加的话,时间复杂度O(n^2 * k),会TLE
套用一下官方的转移公式
进行优化,观察到
超出边界时,dp[i][j] 只比 dp[i][j-1] 多加了一个dp[i-1][j];
没超边界时,dp[i][j] 比 dp[i][j-1] 多了dp[i-1][j],少了dp[i-1][j-i]
所以就可以进行加减运算,优化到O(nk)
继续套用官方公式
另外就是循环j的时候,比如n=2,k=10,并不需要循环那么多次
当i已知时,其逆序对的最大数量也就确定了,全部逆序时,逆序对最多
举例 4 3 2 1,其中43 42 41 32 31 21是其逆序对,发现规律为1到3的累加
推广,对i来说,有i-1个逆序对;对i-1来说,有i-2个逆序对;对1来说,没有逆序对
故长度i的逆序对个数最多为1到i-1的累加,也就是(0+i-1)i/2,首项加末项的和乘项数除以二
可以缩小一部分循环
还有就是取模,几乎每个地方都加了,真的烦
代码
class Solution {
public:
int kInversePairs(int n, int k) {
if(n == 1 && k != 0) return 0;
if(k == 0)