力扣629——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]。

思路

动态规划,用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) 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值