题目
给出两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数。
逆序对的定义如下:对于数组的第i个和第 j个元素,如果满i < j且 a[i] > a[j],则其为一个逆序对;否则不是。
由于答案可能很大,只需要返回 答案 mod 109 + 7 的值。
n 的范围是 [1, 1000] 并且 k 的范围是 [0, 1000]
解题思路
- 首先确定该题需要使用动态规划。
- 为了方便推导公式,先举一个例子:
对于该数组,有逆序数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]。即:
当然,可能有其他的排序方式,但是在逆序数只有0,1,2三种可能。
于是公式变成:
将公式推广:
我们把上述公式j变成j+1,就可以开始推导公式了
上述两式相减:
再把j+1变回j,就可以得出dp[i][j]的公式了:
其中,当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];
}
};