leetCode 629打卡记录2021.11.11

题目

  给出两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数。
  逆序对的定义如下:对于数组的第i个和第 j个元素,如果满i < j且 a[i] > a[j],则其为一个逆序对;否则不是。
  由于答案可能很大,只需要返回 答案 mod 109 + 7 的值。
   n 的范围是 [1, 1000] 并且 k 的范围是 [0, 1000]

解题思路

  1. 首先确定该题需要使用动态规划。
  2. 为了方便推导公式,先举一个例子:
[ 2 , 1 , 3 ]

  对于该数组,有逆序数1(2>3)。
  而如果我们需要再插入一个数,4,想要保证逆序数为2,就需要插入到3的前面。
  假设dp[ i ][ j ],i代表是有i个数,从1到i,j表示逆序数的个数,dp[ i ][ j ]则表示一个有从1到i个数的数组,恰好有j个逆序数的所有可能。
  按上述特殊情况例子表示,dp[4][2] = dp[3][1];
  显然由三个数组组成的数组不止这一个,组成的逆序数也不止有1。但是可以概括出,如果需要保证在4插入后,能到达到dp[4][2]的效果,则需要考虑dp[3]时有三种情况,分别是dp[3][0]、dp[3][1]、dp[3][2]。即:

[ 1 , 2 , 3 ]逆序数为0,4插入到2前面
[ 2 , 1 , 3 ]逆序数为1,4插入到3前面
[ 3 , 1 , 2 ]逆序数为2,4插入到2后面

  当然,可能有其他的排序方式,但是在逆序数只有0,1,2三种可能。
  于是公式变成:

dp[4][2] = dp[3][2] + dp[3][1] + dp[3][0]

  将公式推广:

dp[i][j] = dp[i-1][j] + dp[i-1][j-1] + ... +dp[i-1][j-i+1]

  我们把上述公式j变成j+1,就可以开始推导公式了

dp[i][j+1]=dp[i-1][j+1] + dp[i-1][j] + dp[i-1][j-1] + ... + dp[i-1][j-i+1] + dp[i-1][j-i]

  上述两式相减:

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

  再把j+1变回j,就可以得出dp[i][j]的公式了:

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

  其中,当j>i时,需要上述公式最后一项。得出公式后,把边界问题理清楚,就可以写代码了。

代码

class Solution {
private:
    static constexpr int mod=1e9+7;
public:
    int kInversePairs(int n, int k) {
        vector<vector<int>> dp(n+1,vector<int>(k+1,0));
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            dp[i][0]=1;
            for(int j=1;j<=k;j++){
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
                if(j>=i){
                    dp[i][j]-=dp[i-1][j-i];
                }
                if(dp[i][j]<0){
                    dp[i][j]+=mod;
                }
                dp[i][j]=dp[i][j]%mod;
            }
        }
        return (int)dp[n][k];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值