K Inverse Pairs Array

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:

  1. The integer n is in the range [1, 1000] and k 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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值