Given two integers n
and k
, find how many different arrays consist of numbers from 1
to n
such that there are exactly k
inverse pairs.
We define an inverse pair as following: For ith
and jth
element in the array, if i
< j
and a[i]
> a[j]
then it's an inverse pair; Otherwise, it's not.
Since the answer may be very large, the answer should be modulo 109 + 7.
Example 1:
Input: n = 3, k = 0 Output: 1 Explanation: Only the array [1,2,3] which consists of numbers from 1 to 3 has exactly 0 inverse pair.
Example 2:
Input: n = 3, k = 1 Output: 2 Explanation: The array [1,3,2] and [2,1,3] have exactly 1 inverse pair.
Note:
- The integer
n
is in the range [1, 1000] andk
is in the range [0, 1000].
题目理解:首先定义“反序”:一个数组中前面的数字大于后面的数字。给定n,对于1~n的所有自然数的所有排列,“反序”的个数为k的有多少个。
解题思路:动态规划
一步一步思考这个问题。首先我们定义一个dp数组,dp[i][j]表示0~i的自然数组成的排列中,有j个“反序”的排列的个数,然后我们思考一下递推公式,举个例子[3,1,2]这个数组是dp[3][1]中的一个,当考虑i=4的时候,就是往这个数组的某一个位置插入一个4,因为4大于数组中的每一个数组,因此插入的位置每向左移动一位就会增加一个“反序”,例如[3,1,2,4]有1个“反序”,[3,1,4,2]有2个“反序”,[3,4,1,2]有3个反序,[4,3,1,2]有4个反序,也就是说,[3,1,2]这个数组加上4之后可以变化为1或2或3或4个反序的数组。我们队这个例子进行一般化,一个含有i个数组,j个反序的数组,在加上(i+1)这个数之后,可以变化为含有j,j+1,j+2,...,j+i个反序的数组。所以dp[i+1][j] = sum{dp[i][x]},这里的x满足:x<=j && x+i>=j,也就是说dp[i][x]中的所有数组,都可以通过添加一个i+1得到含有j个反序的数组,x满足的条件是说:i+1这个数字放在原来数组的最左边和最右边的极端情况。到这里,我们初步解决了这个问题,申请一个dp[n+1][k+1],然后按照递推公式循环递推。
考虑这样一个问题,计算dp[i+1]的时候,只需要dp[i]的值,也就是说,每一轮的循环,实际上只需要保存数组中的一行,这样,我们就减少了申请的空间。
再考虑这样一个问题,dp[i+1][j]实际上等于dp[i]这个一维数组的一个连续子数组的和,因此,我们考虑改变dp[i][j]的意义,之前是保存长度为i,反序数目为j的答案,现在我们考虑保存sum{dp[i][0],dp[i][1],....,dp[i][j]},这样,我们计算子数组的和的复杂度就会降低。
注意最后一个小问题,因为存储的都是dp[i][j]%(10e9+7)的值,因此有时候会存在dp[i][6]<dp[i][3]这样的问题,因此,需要对负数执行+(10e9+7)处理
最终代码如下:
class Solution {
int base = 1000000007;
public int kInversePairs(int n, int k) {
long[] cur = new long[k + 1], next;
Arrays.fill(cur, 1);
for(int ct = 1; ct < n; ct++){
next = new long[k + 1];
next[0] = 1;
for(int i = 1; i < k + 1; i++){
int j = i;
next[i] = next[i - 1];
next[i] += cur[i];
if(i - ct - 1 > -1)
next[i] -= cur[i - ct - 1];
if(next[i] < 0){
next[i] += base;
}
next[i] %= base;
}
cur = next;
}
long res;
if(k > 0)
res = (cur[k] - cur[k - 1]);
else
res = cur[k];
if(res < 0)
res += base;
return (int)res;
}
}